diff --git a/Python Waveforms Test/AnalogOut_Sine.py b/Python Waveforms Test/AnalogOut_Sine.py new file mode 100644 index 0000000..36e8eca --- /dev/null +++ b/Python Waveforms Test/AnalogOut_Sine.py @@ -0,0 +1,51 @@ +""" + DWF Python Example + Author: Digilent, Inc. + Revision: 2018-07-19 + + Requires: + Python 2.7, 3 +""" + +from ctypes import * +import time +from dwfconstants import * +import sys + +if sys.platform.startswith("win"): + dwf = cdll.dwf +elif sys.platform.startswith("darwin"): + dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf") +else: + dwf = cdll.LoadLibrary("libdwf.so") + +hdwf = c_int() +channel = c_int(0) + +version = create_string_buffer(16) +dwf.FDwfGetVersion(version) +print("DWF Version: "+str(version.value)) + +dwf.FDwfParamSet(DwfParamOnClose, c_int(0)) # 0 = run, 1 = stop, 2 = shutdown + +#open device +print("Opening first device...") +dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf)) + + +if hdwf.value == hdwfNone.value: + print("failed to open device") + quit() + +dwf.FDwfDeviceAutoConfigureSet(hdwf, c_int(0))# 0 = the device will be configured only when calling FDwf###Configure + +dwf.FDwfAnalogOutNodeEnableSet(hdwf, channel, AnalogOutNodeCarrier, c_bool(True)) +dwf.FDwfAnalogOutNodeFunctionSet(hdwf, channel, AnalogOutNodeCarrier, funcSine) +dwf.FDwfAnalogOutNodeFrequencySet(hdwf, channel, AnalogOutNodeCarrier, c_double(1000)) +dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, channel, AnalogOutNodeCarrier, c_double(1.41)) +dwf.FDwfAnalogOutNodeOffsetSet(hdwf, channel, AnalogOutNodeCarrier, c_double(1.41)) + +print("Generating sine wave...") +dwf.FDwfAnalogOutConfigure(hdwf, channel, c_bool(True)) + +dwf.FDwfDeviceClose(hdwf) diff --git a/Python Waveforms Test/Examples/test_device_info.py b/Python Waveforms Test/Examples/test_device_info.py new file mode 100644 index 0000000..e164139 --- /dev/null +++ b/Python Waveforms Test/Examples/test_device_info.py @@ -0,0 +1,81 @@ +from WF_SDK import device # import instruments + +"""-----------------------------------------------------------------------""" + +# connect to the device +device_data = device.open() + +# check for connection errors +device.check_error(device_data) + +"""-----------------------------------""" + +# set output file name +filename = device_data.name + ".log" +with open(filename, "wt") as f: + + # print device information + print("WaveForms version: " + device_data.version + "\n", file=f) + + # print device name + print("Device name: " + device_data.name + "\n", file=f) + + # print analog input information + print("Analog input information:", file=f) + print("\tchannels: " + str(device_data.analog.input.channel_count), file=f) + print("\tmaximum buffer size: " + str(device_data.analog.input.max_buffer_size), file=f) + print("\tADC resolution: " + str(device_data.analog.input.max_resolution) + " bits", file=f) + print("\trange settable from " + str(device_data.analog.input.min_range) + "V to " + + str(device_data.analog.input.max_range) + "V in " + + str(device_data.analog.input.steps_range) + " steps", file=f) + print("\toffset settable from " + str(device_data.analog.input.min_offset) + "V to " + + str(device_data.analog.input.max_offset) + "V in " + + str(device_data.analog.input.steps_offset) + " steps\n", file=f) + + # print analog output information + print("Analog output information:", file=f) + for channel_index in range(device_data.analog.output.channel_count): + print("\tchannel " + str(channel_index) + ":", file=f) + for node_index in range(device_data.analog.output.node_count[channel_index]): + print("\t\tnode " + str(node_index) + ":", file=f) + print("\t\t\tnode type: " + device_data.analog.output.node_type[channel_index][node_index], file=f) + print("\t\t\tmaximum buffer size: " + str(device_data.analog.output.max_buffer_size[channel_index][node_index]), file=f) + print("\t\t\tamplitude settable from: " + str(device_data.analog.output.min_amplitude[channel_index][node_index]) + "V to " + + str(device_data.analog.output.max_amplitude[channel_index][node_index]) + "V", file=f) + print("\t\t\toffset settable from: " + str(device_data.analog.output.min_offset[channel_index][node_index]) + "V to " + + str(device_data.analog.output.max_offset[channel_index][node_index]) + "V", file=f) + print("\t\t\tfrequency settable from: " + str(device_data.analog.output.min_frequency[channel_index][node_index]) + "Hz to " + + str(device_data.analog.output.max_frequency[channel_index][node_index]) + "Hz\n", file=f) + + # print analog IO information + print("Analog IO information:", file=f) + for channel_index in range(device_data.analog.IO.channel_count): + print("\tchannel " + str(channel_index) + ":", file=f) + print("\t\tchannel name: " + device_data.analog.IO.channel_name[channel_index], file=f) + print("\t\tchannel label: " + device_data.analog.IO.channel_label[channel_index], file=f) + for node_index in range(device_data.analog.IO.node_count[channel_index]): + print("\t\tnode " + str(node_index) + ":", file=f) + print("\t\t\tnode name: " + device_data.analog.IO.node_name[channel_index][node_index], file=f) + print("\t\t\tunit of measurement: " + device_data.analog.IO.node_unit[channel_index][node_index], file=f) + print("\t\t\tsettable from: " + str(device_data.analog.IO.min_set_range[channel_index][node_index]) + " to " + + str(device_data.analog.IO.max_set_range[channel_index][node_index]) + " in " + + str(device_data.analog.IO.set_steps[channel_index][node_index]) + " steps", file=f) + print("\t\t\treadable between: " + str(device_data.analog.IO.min_read_range[channel_index][node_index]) + " to " + + str(device_data.analog.IO.max_read_range[channel_index][node_index]) + " in " + + str(device_data.analog.IO.read_steps[channel_index][node_index]) + " steps\n", file=f) + + + # print digital input information + print("Digital input information:", file=f) + print("\tchannels: " + str(device_data.digital.input.channel_count), file=f) + print("\tmaximum buffer size: " + str(device_data.digital.input.max_buffer_size) + "\n", file=f) + + # print digital output information + print("Digital output information:", file=f) + print("\tchannels: " + str(device_data.digital.output.channel_count), file=f) + print("\tmaximum buffer size: " + str(device_data.digital.output.max_buffer_size), file=f) + +"""-----------------------------------""" + +# close the connection +device.close(device_data) diff --git a/Python Waveforms Test/Main.py b/Python Waveforms Test/Main.py index de6a2b7..e8a846e 100644 --- a/Python Waveforms Test/Main.py +++ b/Python Waveforms Test/Main.py @@ -1,52 +1,22 @@ # -*- coding: utf-8 -*- """ -Created on Tue Oct 25 13:56:45 2022 +Created on Tue Oct 25 14:17:10 2022 @author: Seth """ - -import ctypes # import the C compatible data types -from sys import platform, path # this is needed to check the OS type and get the PATH -from os import sep # OS specific file path separators +from WF_SDK import device # import instruments -# load the dynamic library, get constants path (the path is OS specific) -if platform.startswith("win"): - # on Windows - dwf = ctypes.cdll.dwf - constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" -elif platform.startswith("darwin"): - # on macOS - lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" - dwf = ctypes.cdll.LoadLibrary(lib_path) - constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" -else: - # on Linux - dwf = ctypes.cdll.LoadLibrary("libdwf.so") - constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" +"""-----------------------------------------------------------------------""" -# import constants -path.append(constants_path) -import dwfconstants as constants - -# ============================================================================= - -class data: - """ - stores the device handle and the device name - """ - handle = ctypes.c_int(0) - name = "" +# connect to the device +device_data = device.open() -def open(): - """ - open the first available device - """ - # this is the device handle - it will be used by all functions to "address" the connected device - device_handle = ctypes.c_int() - # connect to the first available device - dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(device_handle)) - data.handle = device_handle - return data - -# ============================================================================= - +"""-----------------------------------""" + +# use instruments here + + +"""-----------------------------------""" + +# close the connection +device.close(device_data) diff --git a/Python Waveforms Test/Samples/Device_Info.py b/Python Waveforms Test/Samples/Device_Info.py new file mode 100644 index 0000000..4ff9bc9 --- /dev/null +++ b/Python Waveforms Test/Samples/Device_Info.py @@ -0,0 +1,179 @@ +""" + DWF Python Example + Author: Digilent, Inc. + Revision: 2018-07-19 + + Requires: + Python 2.7, 3 +""" + +from ctypes import * +import sys +import time + +if sys.platform.startswith("win"): + dwf = cdll.dwf +elif sys.platform.startswith("darwin"): + dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf") +else: + dwf = cdll.LoadLibrary("libdwf.so") + +# check library loading errors +szerr = create_string_buffer(512) +dwf.FDwfGetLastErrorMsg(szerr) +if szerr[0] != b'\0': + print(str(szerr.value)) + +# declare variables +IsInUse = c_bool() +hdwf = c_int() +int0 = c_int() +int1 = c_int() +uint0 = c_uint() +dbl0 = c_double() +dbl1 = c_double() +dbl2 = c_double() +sz0 = create_string_buffer(256) +sz1 = create_string_buffer(256) +cDevice = c_int() +cChannel = c_int() +cNode = c_int() +rgszAnalogOutNode = ["Carrier", "FM", "AM"] + +# declare string variables +devicename = create_string_buffer(64) +serialnum = create_string_buffer(16) + +# print(DWF version +version = create_string_buffer(16) +dwf.FDwfGetVersion(version) +print("DWF Version: "+str(version.value)) + +# enumerate connected devices +dwf.FDwfEnum(c_int(0), byref(cDevice)) +print("Number of Devices: "+str(cDevice.value)) + +# open devices +for iDevice in range(0, cDevice.value): + dwf.FDwfEnumDeviceName(c_int(iDevice), devicename) + dwf.FDwfEnumSN(c_int(iDevice), serialnum) + print("------------------------------") + print("Device "+str(iDevice+1)+" : ") + print("\t" + str(devicename.value)) + print("\t" + str(serialnum.value)) + dwf.FDwfDeviceOpen(c_int(iDevice), byref(hdwf)) + if hdwf.value == 0: + szerr = create_string_buffer(512) + dwf.FDwfGetLastErrorMsg(szerr) + print(szerr.value) + continue + + print("") + dwf.FDwfAnalogInChannelCount(hdwf, byref(int0)) + print("AnalogIn channels: "+str(int0.value)) + + dwf.FDwfAnalogInBufferSizeInfo(hdwf, 0, byref(int0)) + print("Buffer size: "+str(int0.value)) + + dwf.FDwfAnalogInBitsInfo(hdwf, byref(int0)) + print("ADC bits: "+str(int0.value)) + + dwf.FDwfAnalogInChannelRangeInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2)) + print("Range from "+str(dbl0.value)+" to "+str(dbl1.value)+" in "+str(dbl2.value)+" steps") + + dwf.FDwfAnalogInChannelOffsetInfo(hdwf, byref(dbl0), byref(dbl1), byref(dbl2)) + print("Offset from "+str(dbl0.value)+" to "+str(dbl1.value)+" in "+str(dbl2.value)+" steps") + + + print("") + dwf.FDwfAnalogOutCount(hdwf, byref(cChannel)) + print("AnalogOut channels: "+str(cChannel.value)) + + for iChannel in range(0, cChannel.value): + print("Channel "+str(iChannel+1)) + + fsnodes = c_int() + dwf.FDwfAnalogOutNodeInfo(hdwf, c_int(iChannel), byref(fsnodes)) + + for iNode in range(0, 2): + if (fsnodes.value & (1<1 : + print("Setting from "+str(dbl0.value)+" to "+str(dbl1.value)+" in "+str(int0.value)+" steps") + + dwf.FDwfAnalogIOChannelNodeStatusInfo(hdwf, iChannel, iNode, byref(dbl0), byref(dbl1), byref(int0)) + if int0.value==1 : + if dbl0.value == dbl1.value : + print("Constant input "+str(dbl0.value)) + else: + print("Input range from "+str(dbl0.value)+" to "+str(dbl1.value)) + elif int0.value>1 : + print("Reading from "+str(dbl0.value)+" to "+str(dbl1.value)+" in "+str(int0.value)+" steps") + + + print("") + dwf.FDwfDigitalInBitsInfo(hdwf, byref(int0)) + print("DigitalIn channels: "+str(int0.value)) + + dwf.FDwfDigitalInBufferSizeInfo(hdwf, byref(int0)) + print("Buffer size: "+str(int0.value)) + + + print("") + dwf.FDwfDigitalOutCount(hdwf, byref(int0)) + print("DigitalOut channels: "+str(int0.value)) + + dwf.FDwfDigitalOutDataInfo(hdwf, c_int(0), byref(int0)) + print("Custom size: "+str(int0.value)) + + + print("") + print("DigitalIO information:") + dwf.FDwfDigitalIOOutputEnableInfo(hdwf, byref(uint0)) + print("OE Mask: "+hex(uint0.value)) + dwf.FDwfDigitalIOOutputInfo(hdwf, byref(uint0)) + print("Output : "+hex(uint0.value)) + dwf.FDwfDigitalIOInputInfo(hdwf, byref(uint0)) + print("Input : "+hex(uint0.value)) + +# ensure all devices are closed +dwf.FDwfDeviceCloseAll() diff --git a/Python Waveforms Test/Stuff.py b/Python Waveforms Test/Stuff.py new file mode 100644 index 0000000..c9a142c --- /dev/null +++ b/Python Waveforms Test/Stuff.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Oct 25 13:56:45 2022 + +@author: Seth +""" + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants + +# ============================================================================= + +class data: + """ + stores the device handle and the device name + """ + handle = ctypes.c_int(0) + name = "" + +def open(): + """ + open the first available device + """ + # this is the device handle - it will be used by all functions to "address" the connected device + device_handle = ctypes.c_int() + # connect to the first available device + dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(device_handle)) + data.handle = device_handle + return data + +# ============================================================================= + +class function: + """ function names """ + custom = constants.funcCustom + sine = constants.funcSine + square = constants.funcSquare + triangle = constants.funcTriangle + noise = constants.funcNoise + dc = constants.funcDC + pulse = constants.funcPulse + trapezium = constants.funcTrapezium + sine_power = constants.funcSinePower + ramp_up = constants.funcRampUp + ramp_down = constants.funcRampDown + +def generate(device_data, channel, function, offset, frequency=1e03, amplitude=1, symmetry=50, wait=0, run_time=0, repeat=0, data=[]): + """ + generate an analog signal + parameters: - device data + - the selected wavegen channel (1-2) + - function - possible: custom, sine, square, triangle, noise, ds, pulse, trapezium, sine_power, ramp_up, ramp_down + - offset voltage in Volts + - frequency in Hz, default is 1KHz + - amplitude in Volts, default is 1V + - signal symmetry in percentage, default is 50% + - wait time in seconds, default is 0s + - run time in seconds, default is infinite (0) + - repeat count, default is infinite (0) + - data - list of voltages, used only if function=custom, default is empty + """ + # enable channel + channel = ctypes.c_int(channel - 1) + dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_bool(True)) + + # set function type + dwf.FDwfAnalogOutNodeFunctionSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, function) + + # load data if the function type is custom + if function == constants.funcCustom: + data_length = len(data) + buffer = (ctypes.c_double * data_length)() + for index in range(0, len(buffer)): + buffer[index] = ctypes.c_double(data[index]) + dwf.FDwfAnalogOutNodeDataSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, buffer, ctypes.c_int(data_length)) + + # set frequency + dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(frequency)) + + # set amplitude or DC voltage + dwf.FDwfAnalogOutNodeAmplitudeSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(amplitude)) + + # set offset + dwf.FDwfAnalogOutNodeOffsetSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(offset)) + + # set symmetry + dwf.FDwfAnalogOutNodeSymmetrySet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(symmetry)) + + # set running time limit + dwf.FDwfAnalogOutRunSet(device_data.handle, channel, ctypes.c_double(run_time)) + + # set wait time before start + dwf.FDwfAnalogOutWaitSet(device_data.handle, channel, ctypes.c_double(wait)) + + # set number of repeating cycles + dwf.FDwfAnalogOutRepeatSet(device_data.handle, channel, ctypes.c_int(repeat)) + + # start + dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(True)) + return + +def close(device_data, channel=0): + """ + reset a wavegen channel, or all channels (channel=0) + """ + channel = ctypes.c_int(channel - 1) + dwf.FDwfAnalogOutReset(device_data.handle, channel) + return + +def close(device_data): + """ + close a specific device + """ + dwf.FDwfDeviceClose(device_data.handle) + return + +# =================================== + +myDevice = open() + + + +#close(device) diff --git a/Python Waveforms Test/WF_SDK/__init__.py b/Python Waveforms Test/WF_SDK/__init__.py new file mode 100644 index 0000000..f75d549 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/__init__.py @@ -0,0 +1,17 @@ +""" +This module realizes communication with Digilent Test & Measurement devices +""" + +from WF_SDK import device +from WF_SDK import scope +from WF_SDK import wavegen +from WF_SDK import supplies +from WF_SDK import dmm +from WF_SDK import logic +from WF_SDK import pattern +from WF_SDK import static +from WF_SDK import protocol + +from WF_SDK import tools + +from WF_SDK.device import error, warning diff --git a/Python Waveforms Test/WF_SDK/device.py b/Python Waveforms Test/WF_SDK/device.py new file mode 100644 index 0000000..49c03f9 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/device.py @@ -0,0 +1,466 @@ +""" DEVICE CONTROL FUNCTIONS: open, check_error, close, temperature """ + +""" +import ctypes # import the C compatible data types +import WF_SDK.dwfconstants as constants # import every constant +from sys import platform # this is needed to check the OS type + +# load the dynamic library (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf +elif platform.startswith("darwin"): + # on macOS + dwf = ctypes.cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf") +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + +""" + +"""-----------------------------------------------------------------------""" + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators +import inspect # caller function data + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants + +"""-----------------------------------------------------------------------""" + +""" +def open(): + ''' + open the first available device + ''' + # this is the device handle - it will be used by all functions to "address" the connected device + device_handle = ctypes.c_int() + + # connect to the first available device + dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(device_handle)) + data.handle = device_handle + data.name = device_name + return data +""" + +"""-----------------------------------------------------------------------""" + +class error(Exception): + """ + WaveForms SDK error + """ + def __init__(self, message, function, instrument): + self.message = message + self.function = function + self.instrument = instrument + return + def __str__(self): + return "Error: " + self.instrument + " -> " + self.function + " -> " + self.message + +class warning(Exception): + """ + WaveForms SDK warning, or non-fatal error + """ + def __init__(self, message, function, instrument): + self.message = message + self.function = function + self.instrument = instrument + return + def __str__(self): + return "Warning: " + self.instrument + " -> " + self.function + " -> " + self.message + +class data: + """ stores the device handle, the device name and the device data """ + handle = ctypes.c_int(0) + name = "" + version = "" + class analog: + class input: + channel_count = 0 + max_buffer_size = 0 + max_resolution = 0 + min_range = 0 + max_range = 0 + steps_range = 0 + min_offset = 0 + max_offset = 0 + steps_offset = 0 + class output: + channel_count = 0 + node_count = [] + node_type = [] + max_buffer_size = [] + min_amplitude = [] + max_amplitude = [] + min_offset = [] + max_offset = [] + min_frequency = [] + max_frequency = [] + class IO: + channel_count = 0 + node_count = [] + channel_name = [] + channel_label = [] + node_name = [] + node_unit = [] + min_set_range = [] + max_set_range = [] + min_read_range = [] + max_read_range = [] + set_steps = [] + read_steps = [] + class digital: + class input: + channel_count = 0 + max_buffer_size = 0 + class output: + channel_count = 0 + max_buffer_size = 0 + +"""-----------------------------------------------------------------------""" + +def open(device=None, config=0): + """ + open a specific device + + parameters: - device type: None (first device), "Analog Discovery", "Analog Discovery 2", "Analog Discovery Studio", "Digital Discovery", "Analog Discovery Pro 3X50", "Analog Discovery Pro 5250" + - configuration: 0 = auto, default = auto + + returns: - device data + """ + device_names = [("Analog Discovery", constants.devidDiscovery), ("Analog Discovery 2", constants.devidDiscovery2), + ("Analog Discovery Studio", constants.devidDiscovery2), ("Digital Discovery", constants.devidDDiscovery), + ("Analog Discovery Pro 3X50", constants.devidADP3X50), ("Analog Discovery Pro 5250", constants.devidADP5250)] + + # decode device names + device_type = constants.enumfilterAll + for pair in device_names: + if pair[0] == device: + device_type = pair[1] + break + + # count devices + device_count = ctypes.c_int() + dwf.FDwfEnum(device_type, ctypes.byref(device_count)) + + # check for connected devices + if device_count.value <= 0: + if device_type.value == 0: + raise error("There are no connected devices", "open", "device") + else: + raise error("Error: There is no " + device + " connected", "open", "device") + + # this is the device handle - it will be used by all functions to "address" the connected device + device_handle = ctypes.c_int(0) + + # connect to the first available device + index = 0 + while device_handle.value == 0 and index < device_count.value: + dwf.FDwfDeviceConfigOpen(ctypes.c_int(index), ctypes.c_int(config), ctypes.byref(device_handle)) + index += 1 # increment the index and try again if the device is busy + + # check connected device type + device_name = "" + if device_handle.value != 0: + device_id = ctypes.c_int() + device_rev = ctypes.c_int() + dwf.FDwfEnumDeviceType(ctypes.c_int(index - 1), ctypes.byref(device_id), ctypes.byref(device_rev)) + + # decode device id + for pair in device_names: + if pair[1].value == device_id.value: + device_name = pair[0] + break + + # check for errors + # if the device handle is empty after a connection attempt + if device_handle == constants.hdwfNone: + # check for errors + err_nr = ctypes.c_int() # variable for error number + dwf.FDwfGetLastError(ctypes.byref(err_nr)); # get error number + # if there is an error + if err_nr != constants.dwfercNoErc: + # check the error message + check_error() + + global data + data.handle = device_handle + data.name = device_name + data = __get_info__(data) + return data + +"""-----------------------------------------------------------------------""" + +def check_error(): + """ + check for errors + """ + err_msg = ctypes.create_string_buffer(512) # variable for the error message + dwf.FDwfGetLastErrorMsg(err_msg) # get the error message + err_msg = err_msg.value.decode("ascii") # format the message + if err_msg != "": + err_func = inspect.stack()[1].function # get caller function + err_inst = inspect.stack()[1].filename # get caller file name + # delete the extension + err_inst = err_inst.split('.')[0] + # delete the path + path_list = err_inst.split('/') + err_inst = path_list[-1] + path_list = err_inst.split('\\') + err_inst = path_list[-1] + raise error(err_msg, err_func, err_inst) + return + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + close a specific device + """ + if device_data.handle != 0: + dwf.FDwfDeviceClose(device_data.handle) + data.handle = ctypes.c_int(0) + data.name = "" + return + +"""-----------------------------------------------------------------------""" + +def temperature(device_data): + """ + return the board temperature + """ + channel = -1 + node = -1 + # find the system monitor + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "System": + channel = channel_index + break + if channel < 0: + return 0 + + # find the temperature node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Temp": + node = node_index + break + if node < 0: + return 0 + + # read the temperature + if dwf.FDwfAnalogIOStatus(device_data.handle) == 0: + check_error() + temperature = ctypes.c_double() + if dwf.FDwfAnalogIOChannelNodeStatus(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.byref(temperature)) == 0: + check_error() + return temperature.value + +"""-----------------------------------------------------------------------""" + +def __get_info__(device_data): + """ + get and return device information + """ + # check WaveForms version + version = ctypes.create_string_buffer(16) + if dwf.FDwfGetVersion(version) == 0: + check_error() + device_data.version = str(version.value)[2:-1] + + # define temporal variables + temp1 = ctypes.c_int() + temp2 = ctypes.c_int() + temp3 = ctypes.c_int() + + # analog input information + # channel count + if dwf.FDwfAnalogInChannelCount(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.input.channel_count = temp1.value + # buffer size + if dwf.FDwfAnalogInBufferSizeInfo(device_data.handle, 0, ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.input.max_buffer_size = temp1.value + # ADC resolution + if dwf.FDwfAnalogInBitsInfo(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.input.max_resolution = temp1.value + # range information + temp1 = ctypes.c_double() + temp2 = ctypes.c_double() + temp3 = ctypes.c_double() + if dwf.FDwfAnalogInChannelRangeInfo(device_data.handle, ctypes.byref(temp1), ctypes.byref(temp2), ctypes.byref(temp3)) == 0: + check_error() + device_data.analog.input.min_range = temp1.value + device_data.analog.input.max_range = temp2.value + device_data.analog.input.steps_range = int(temp3.value) + # offset information + if dwf.FDwfAnalogInChannelOffsetInfo(device_data.handle, ctypes.byref(temp1), ctypes.byref(temp2), ctypes.byref(temp3)) == 0: + check_error() + device_data.analog.input.min_offset = temp1.value + device_data.analog.input.max_offset = temp2.value + device_data.analog.input.steps_offset = int(temp3.value) + + # analog output information + temp1 = ctypes.c_int() + if dwf.FDwfAnalogOutCount(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.output.channel_count = temp1.value + for channel_index in range(device_data.analog.output.channel_count): + # check node types and node count + temp1 = ctypes.c_int() + if dwf.FDwfAnalogOutNodeInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.byref(temp1)) == 0: + check_error() + templist = [] + for node_index in range(3): + if ((1 << node_index) & int(temp1.value)) == 0: + continue + elif node_index == constants.AnalogOutNodeCarrier.value: + templist.append("carrier") + elif node_index == constants.AnalogOutNodeFM.value: + templist.append("FM") + elif node_index == constants.AnalogOutNodeAM.value: + templist.append("AM") + device_data.analog.output.node_type.append(templist) + device_data.analog.output.node_count.append(len(templist)) + # buffer size + templist = [] + for node_index in range(device_data.analog.output.node_count[channel_index]): + if dwf.FDwfAnalogOutNodeDataInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), 0, ctypes.byref(temp1)) == 0: + check_error() + templist.append(temp1.value) + device_data.analog.output.max_buffer_size.append(templist) + # amplitude information + templist1 = [] + templist2 = [] + temp1 = ctypes.c_double() + temp2 = ctypes.c_double() + for node_index in range(device_data.analog.output.node_count[channel_index]): + if dwf.FDwfAnalogOutNodeAmplitudeInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), ctypes.byref(temp1), ctypes.byref(temp2)) == 0: + check_error() + templist1.append(temp1.value) + templist2.append(temp2.value) + device_data.analog.output.min_amplitude.append(templist1) + device_data.analog.output.max_amplitude.append(templist2) + # offset information + templist1 = [] + templist2 = [] + for node_index in range(device_data.analog.output.node_count[channel_index]): + if dwf.FDwfAnalogOutNodeOffsetInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), ctypes.byref(temp1), ctypes.byref(temp2)) == 0: + check_error() + templist1.append(temp1.value) + templist2.append(temp2.value) + device_data.analog.output.min_offset.append(templist1) + device_data.analog.output.max_offset.append(templist2) + # frequency information + templist1 = [] + templist2 = [] + for node_index in range(device_data.analog.output.node_count[channel_index]): + if dwf.FDwfAnalogOutNodeFrequencyInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), ctypes.byref(temp1), ctypes.byref(temp2)) == 0: + check_error() + templist1.append(temp1.value) + templist2.append(temp2.value) + device_data.analog.output.min_frequency.append(templist1) + device_data.analog.output.max_frequency.append(templist2) + + # analog IO information + # channel count + temp1 = ctypes.c_int() + if dwf.FDwfAnalogIOChannelCount(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.IO.channel_count = temp1.value + for channel_index in range(device_data.analog.IO.channel_count): + # channel names and labels + temp1 = ctypes.create_string_buffer(256) + temp2 = ctypes.create_string_buffer(256) + if dwf.FDwfAnalogIOChannelName(device_data.handle, ctypes.c_int(channel_index), temp1, temp2) == 0: + check_error() + device_data.analog.IO.channel_name.append(str(temp1.value)[2:-1]) + device_data.analog.IO.channel_label.append(str(temp2.value)[2:-1]) + # check node count + temp1 = ctypes.c_int() + if dwf.FDwfAnalogIOChannelInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.byref(temp1)) == 0: + check_error() + device_data.analog.IO.node_count.append(temp1.value) + # node names and units + templist1 = [] + templist2 = [] + for node_index in range(device_data.analog.IO.node_count[channel_index]): + temp1 = ctypes.create_string_buffer(256) + temp2 = ctypes.create_string_buffer(256) + if dwf.FDwfAnalogIOChannelNodeName(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), temp1, temp2) == 0: + check_error() + templist1.append(str(temp1.value)[2:-1]) + templist2.append(str(temp2.value)[2:-1]) + device_data.analog.IO.node_name.append(templist1) + device_data.analog.IO.node_unit.append(templist2) + # node write info + templist1 = [] + templist2 = [] + templist3 = [] + temp1 = ctypes.c_double() + temp2 = ctypes.c_double() + temp3 = ctypes.c_int() + for node_index in range(device_data.analog.IO.node_count[channel_index]): + if dwf.FDwfAnalogIOChannelNodeSetInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), ctypes.byref(temp1), ctypes.byref(temp2), ctypes.byref(temp3)) == 0: + check_error() + templist1.append(temp1.value) + templist2.append(temp2.value) + templist3.append(temp3.value) + device_data.analog.IO.min_set_range.append(templist1) + device_data.analog.IO.max_set_range.append(templist2) + device_data.analog.IO.set_steps.append(templist3) + # node read info + templist1 = [] + templist2 = [] + templist3 = [] + for node_index in range(device_data.analog.IO.node_count[channel_index]): + if dwf.FDwfAnalogIOChannelNodeStatusInfo(device_data.handle, ctypes.c_int(channel_index), ctypes.c_int(node_index), ctypes.byref(temp1), ctypes.byref(temp2), ctypes.byref(temp3)) == 0: + check_error() + templist1.append(temp1.value) + templist2.append(temp2.value) + templist3.append(temp3.value) + device_data.analog.IO.min_read_range.append(templist1) + device_data.analog.IO.max_read_range.append(templist2) + device_data.analog.IO.read_steps.append(templist3) + + # digital input information + # channel count + temp1 = ctypes.c_int() + if dwf.FDwfDigitalInBitsInfo(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.digital.input.channel_count = temp1.value + # buffer size + if dwf.FDwfDigitalInBufferSizeInfo(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.digital.input.max_buffer_size = temp1.value + + # digital output information + # channel count + if dwf.FDwfDigitalOutCount(device_data.handle, ctypes.byref(temp1)) == 0: + check_error() + device_data.digital.output.channel_count = temp1.value + # buffer size + if dwf.FDwfDigitalOutDataInfo(device_data.handle, ctypes.c_int(0), ctypes.byref(temp1)) == 0: + check_error() + device_data.digital.output.max_buffer_size = temp1.value + + return device_data diff --git a/Python Waveforms Test/WF_SDK/dmm.py b/Python Waveforms Test/WF_SDK/dmm.py new file mode 100644 index 0000000..e42538b --- /dev/null +++ b/Python Waveforms Test/WF_SDK/dmm.py @@ -0,0 +1,149 @@ +""" DIGITAL MULTIMETER CONTROL FUNCTIONS: open, measure, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class mode: + """ DMM modes """ + ac_voltage = constants.DwfDmmACVoltage + dc_voltage = constants.DwfDmmDCVoltage + ac_high_current = constants.DwfDmmACCurrent + dc_high_current = constants.DwfDmmDCCurrent + ac_low_current = constants.DwfDmmACLowCurrent + dc_low_current = constants.DwfDmmDCLowCurrent + resistance = constants.DwfDmmResistance + continuity = constants.DwfDmmContinuity + diode = constants.DwfDmmDiode + temperature = constants.DwfDmmTemperature + +"""-----------------------------------------------------------------------""" + +class data: + """ storers instrument information """ + __channel__ = -1 + class __nodes__: + __enable__ = -1 + __mode__ = -1 + __range__ = -1 + __meas__ = -1 + __raw__ = -1 + __input__ = -1 + +"""-----------------------------------------------------------------------""" + +def open(device_data): + """ + initialize the digital multimeter + """ + # find channel + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "DMM": + data.__channel__ = channel_index + break + + # find nodes + if data.__channel__ >= 0: + for node_index in range(device_data.analog.IO.node_count[data.__channel__]): + if device_data.analog.IO.node_name[data.__channel__][node_index] == "Enable": + data.__nodes__.__enable__ = node_index + elif device_data.analog.IO.node_name[data.__channel__][node_index] == "Mode": + data.__nodes__.__mode__ = node_index + elif device_data.analog.IO.node_name[data.__channel__][node_index] == "Range": + data.__nodes__.__range__ = node_index + elif device_data.analog.IO.node_name[data.__channel__][node_index] == "Meas": + data.__nodes__.__meas__ = node_index + elif device_data.analog.IO.node_name[data.__channel__][node_index] == "Raw": + data.__nodes__.__raw__ = node_index + elif device_data.analog.IO.node_name[data.__channel__][node_index] == "Input": + data.__nodes__.__input__ = node_index + + # enable the DMM + if data.__channel__ >= 0 and data.__nodes__.__enable__ >= 0: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__enable__), ctypes.c_double(1.0)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def measure(device_data, mode, range=0, high_impedance=False): + """ + measure a voltage/current/resistance/continuity/temperature + + parameters: - device data + - mode: dmm.mode.ac_voltage/dc_voltage/ac_high_current/dc_high_current/ac_low_current/dc_low_current/resistance/continuity/diode/temperature + - range: voltage/current/resistance/temperature range, 0 means auto, default is auto + - high_impedance: input impedance for DC voltage measurement, False means 10MΩ, True means 10GΩ, default is 10MΩ + + returns: - the measured value in V/A/Ω/°C, or None on error + """ + if data.__channel__ >= 0: + # set input impedance + if data.__nodes__.__input__ >= 0: + if high_impedance: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__input__), ctypes.c_double(1)) == 0: + check_error() + else: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__input__), ctypes.c_double(0)) == 0: + check_error() + + # set mode + if data.__nodes__.__mode__ >= 0: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__mode__), mode) == 0: + check_error() + + # set range + if data.__nodes__.__range__ >= 0: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__range__), range) == 0: + check_error() + + # fetch analog IO status + if dwf.FDwfAnalogIOStatus(device_data.handle) == 0: + # signal error + check_error() + return None + + # get reading + if data.__nodes__.__meas__ >= 0: + measurement = ctypes.c_double() + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__meas__), ctypes.byref(measurement)) == 0: + check_error() + return measurement.value + return None + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the instrument + """ + # disable the DMM + if data.__channel__ >= 0 and data.__nodes__.__enable__ >= 0: + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(data.__channel__), ctypes.c_int(data.__nodes__.__enable__), ctypes.c_double(0)) == 0: + check_error() + # reset the instrument + if dwf.FDwfAnalogIOReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/logic.py b/Python Waveforms Test/WF_SDK/logic.py new file mode 100644 index 0000000..a7b475b --- /dev/null +++ b/Python Waveforms Test/WF_SDK/logic.py @@ -0,0 +1,174 @@ +""" LOGIC ANALYZER CONTROL FUNCTIONS: open, trigger, record, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class data: + """ stores the sampling frequency and the buffer size """ + sampling_frequency = 100e06 + buffer_size = 4096 + max_buffer_size = 0 + +"""-----------------------------------------------------------------------""" + +def open(device_data, sampling_frequency=100e06, buffer_size=0): + """ + initialize the logic analyzer + + parameters: - device data + - sampling frequency in Hz, default is 100MHz + - buffer size, default is 0 (maximum) + """ + # set global variables + data.sampling_frequency = sampling_frequency + data.max_buffer_size = device_data.digital.input.max_buffer_size + + # get internal clock frequency + internal_frequency = ctypes.c_double() + if dwf.FDwfDigitalInInternalClockInfo(device_data.handle, ctypes.byref(internal_frequency)) == 0: + check_error() + + # set clock frequency divider (needed for lower frequency input signals) + if dwf.FDwfDigitalInDividerSet(device_data.handle, ctypes.c_int(int(internal_frequency.value / sampling_frequency))) == 0: + check_error() + + # set 16-bit sample format + if dwf.FDwfDigitalInSampleFormatSet(device_data.handle, ctypes.c_int(16)) == 0: + check_error() + + # set buffer size + if buffer_size == 0: + buffer_size = data.max_buffer_size + data.buffer_size = buffer_size + if dwf.FDwfDigitalInBufferSizeSet(device_data.handle, ctypes.c_int(buffer_size)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def trigger(device_data, enable, channel, position=0, timeout=0, rising_edge=True, length_min=0, length_max=20, count=0): + """ + set up triggering + + parameters: - device data + - enable - True or False to enable, or disable triggering + - channel - the selected DIO line number to use as trigger source + - buffer size, the default is 4096 + - position - prefill size, the default is 0 + - timeout - auto trigger time, the default is 0 + - rising_edge - set True for rising edge, False for falling edge, the default is rising edge + - length_min - trigger sequence minimum time in seconds, the default is 0 + - length_max - trigger sequence maximum time in seconds, the default is 20 + - count - instance count, the default is 0 (immediate) + """ + # set trigger source to digital I/O lines, or turn it off + if enable: + if dwf.FDwfDigitalInTriggerSourceSet(device_data.handle, constants.trigsrcDetectorDigitalIn) == 0: + check_error() + else: + if dwf.FDwfDigitalInTriggerSourceSet(device_data.handle, constants.trigsrcNone) == 0: + check_error() + return + + # set starting position and prefill + position = min(data.buffer_size, max(0, position)) + if dwf.FDwfDigitalInTriggerPositionSet(device_data.handle, ctypes.c_int(data.buffer_size - position)) == 0: + check_error() + if dwf.FDwfDigitalInTriggerPrefillSet(device_data.handle, ctypes.c_int(position)) == 0: + check_error() + + # set trigger condition + channel = ctypes.c_int(1 << channel) + if not rising_edge: + if dwf.FDwfDigitalInTriggerSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0)) == 0: + check_error() + if dwf.FDwfDigitalInTriggerResetSet(device_data.handle, ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), channel) == 0: + check_error() + else: + if dwf.FDwfDigitalInTriggerSet(device_data.handle, ctypes.c_int(0), channel, ctypes.c_int(0), ctypes.c_int(0)) == 0: + check_error() + if dwf.FDwfDigitalInTriggerResetSet(device_data.handle, ctypes.c_int(0), ctypes.c_int(0), channel, ctypes.c_int(0)) == 0: + check_error() + + # set auto triggering + if dwf.FDwfDigitalInTriggerAutoTimeoutSet(device_data.handle, ctypes.c_double(timeout)) == 0: + check_error() + + # set sequence length to activate trigger + if dwf.FDwfDigitalInTriggerLengthSet(device_data.handle, ctypes.c_double(length_min), ctypes.c_double(length_max), ctypes.c_int(0)) == 0: + check_error() + + # set event counter + if dwf.FDwfDigitalInTriggerCountSet(device_data.handle, ctypes.c_int(count), ctypes.c_int(0)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def record(device_data, channel): + """ + initialize the logic analyzer + + parameters: - device data + - channel - the selected DIO line number + + returns: - a list with the recorded logic values + """ + # set up the instrument + if dwf.FDwfDigitalInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(True)) == 0: + check_error() + + # read data to an internal buffer + while True: + status = ctypes.c_byte() # variable to store buffer status + if dwf.FDwfDigitalInStatus(device_data.handle, ctypes.c_bool(True), ctypes.byref(status)) == 0: + check_error() + + if status.value == constants.stsDone.value: + # exit loop when finished + break + + # get samples + buffer = (ctypes.c_uint16 * data.buffer_size)() + if dwf.FDwfDigitalInStatusData(device_data.handle, buffer, ctypes.c_int(2 * data.buffer_size)) == 0: + check_error() + + # convert buffer to list of lists of integers + result = [] + for point in buffer: + result.append((int(point) & (1 << channel)) >> channel) + + return result + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the instrument + """ + if dwf.FDwfDigitalInReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/pattern.py b/Python Waveforms Test/WF_SDK/pattern.py new file mode 100644 index 0000000..625e639 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/pattern.py @@ -0,0 +1,205 @@ +""" PATTERN GENERATOR CONTROL FUNCTIONS: generate, close, enable, disable """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class function: + """ function names """ + pulse = constants.DwfDigitalOutTypePulse + custom = constants.DwfDigitalOutTypeCustom + random = constants.DwfDigitalOutTypeRandom + +"""-----------------------------------------------------------------------""" + +class trigger_source: + """ trigger source names """ + none = constants.trigsrcNone + analog = constants.trigsrcDetectorAnalogIn + digital = constants.trigsrcDetectorDigitalIn + external = [None, constants.trigsrcExternal1, constants.trigsrcExternal2, constants.trigsrcExternal3, constants.trigsrcExternal4] + +"""-----------------------------------------------------------------------""" + +class idle_state: + """ channel idle states """ + initial = constants.DwfDigitalOutIdleInit + high = constants.DwfDigitalOutIdleHigh + low = constants.DwfDigitalOutIdleLow + high_impedance = constants.DwfDigitalOutIdleZet + +"""-----------------------------------------------------------------------""" + +def generate(device_data, channel, function, frequency, duty_cycle=50, data=[], wait=0, repeat=0, run_time=0, idle=idle_state.initial, trigger_enabled=False, trigger_source=trigger_source.none, trigger_edge_rising=True): + """ + generate a logic signal + + parameters: - channel - the selected DIO line number + - function - possible: pulse, custom, random + - frequency in Hz + - duty cycle in percentage, used only if function = pulse, default is 50% + - data list, used only if function = custom, default is empty + - wait time in seconds, default is 0 seconds + - repeat count, default is infinite (0) + - run_time: in seconds, 0=infinite, "auto"=auto + - idle - possible: initial, high, low, high_impedance, default = initial + - trigger_enabled - include/exclude trigger from repeat cycle + - trigger_source - possible: none, analog, digital, external[1-4] + - trigger_edge_rising - True means rising, False means falling, None means either, default is rising + """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + + # get internal clock frequency + internal_frequency = ctypes.c_double() + if dwf.FDwfDigitalOutInternalClockInfo(device_data.handle, ctypes.byref(internal_frequency)) == 0: + check_error() + + # get counter value range + counter_limit = ctypes.c_uint() + if dwf.FDwfDigitalOutCounterInfo(device_data.handle, ctypes.c_int(channel), ctypes.c_int(0), ctypes.byref(counter_limit)) == 0: + check_error() + + # calculate the divider for the given signal frequency + if function == constants.DwfDigitalOutTypePulse: + divider = int(-(-(internal_frequency.value / frequency) // counter_limit.value)) + else: + divider = int(internal_frequency.value / frequency) + + # enable the respective channel + if dwf.FDwfDigitalOutEnableSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(1)) == 0: + check_error() + + # set output type + if dwf.FDwfDigitalOutTypeSet(device_data.handle, ctypes.c_int(channel), function) == 0: + check_error() + + # set frequency + if dwf.FDwfDigitalOutDividerSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(divider)) == 0: + check_error() + + # set idle state + if dwf.FDwfDigitalOutIdleSet(device_data.handle, ctypes.c_int(channel), idle) == 0: + check_error() + + # set PWM signal duty cycle + if function == constants.DwfDigitalOutTypePulse: + # calculate counter steps to get the required frequency + steps = int(round(internal_frequency.value / frequency / divider)) + # calculate steps for low and high parts of the period + high_steps = int(steps * duty_cycle / 100) + low_steps = int(steps - high_steps) + if dwf.FDwfDigitalOutCounterSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(low_steps), ctypes.c_int(high_steps)) == 0: + check_error() + + # load custom signal data + elif function == constants.DwfDigitalOutTypeCustom: + # format data + buffer = (ctypes.c_ubyte * ((len(data) + 7) >> 3))(0) + for index in range(len(data)): + if data[index] != 0: + buffer[index >> 3] |= 1 << (index & 7) + + # load data + if dwf.FDwfDigitalOutDataSet(device_data.handle, ctypes.c_int(channel), ctypes.byref(buffer), ctypes.c_int(len(data))) == 0: + check_error() + + # calculate run length + if run_time == "auto": + run_time = len(data) / frequency + + # set wait time + if dwf.FDwfDigitalOutWaitSet(device_data.handle, ctypes.c_double(wait)) == 0: + check_error() + + # set repeat count + if dwf.FDwfDigitalOutRepeatSet(device_data.handle, ctypes.c_int(repeat)) == 0: + check_error() + + # set run length + if dwf.FDwfDigitalOutRunSet(device_data.handle, ctypes.c_double(run_time)) == 0: + check_error() + + # enable triggering + if dwf.FDwfDigitalOutRepeatTriggerSet(device_data.handle, ctypes.c_int(trigger_enabled)) == 0: + check_error() + + if trigger_enabled: + # set trigger source + if dwf.FDwfDigitalOutTriggerSourceSet(device_data.handle, trigger_source) == 0: + check_error() + + # set trigger slope + if trigger_edge_rising == True: + # rising edge + if dwf.FDwfDigitalOutTriggerSlopeSet(device_data.handle, constants.DwfTriggerSlopeRise) == 0: + check_error() + elif trigger_edge_rising == False: + # falling edge + if dwf.FDwfDigitalOutTriggerSlopeSet(device_data.handle, constants.DwfTriggerSlopeFall) == 0: + check_error() + elif trigger_edge_rising == None: + # either edge + if dwf.FDwfDigitalOutTriggerSlopeSet(device_data.handle, constants.DwfTriggerSlopeEither) == 0: + check_error() + + # start generating the signal + if dwf.FDwfDigitalOutConfigure(device_data.handle, ctypes.c_int(True)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the instrument + """ + if dwf.FDwfDigitalOutReset(device_data.handle) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def enable(device_data, channel): + """ enables a digital output channel """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + if dwf.FDwfDigitalOutEnableSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(1)) == 0: + check_error() + if dwf.FDwfDigitalOutConfigure(device_data.handle, ctypes.c_int(True)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def disable(device_data, channel): + """ disables a digital output channel """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + if dwf.FDwfDigitalOutEnableSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(0)) == 0: + check_error() + if dwf.FDwfDigitalOutConfigure(device_data.handle, ctypes.c_int(True)) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/protocol/__init__.py b/Python Waveforms Test/WF_SDK/protocol/__init__.py new file mode 100644 index 0000000..d781b4c --- /dev/null +++ b/Python Waveforms Test/WF_SDK/protocol/__init__.py @@ -0,0 +1,7 @@ +""" +This module controls the protocol instrument +""" + +from WF_SDK.protocol import i2c +from WF_SDK.protocol import spi +from WF_SDK.protocol import uart diff --git a/Python Waveforms Test/WF_SDK/protocol/i2c.py b/Python Waveforms Test/WF_SDK/protocol/i2c.py new file mode 100644 index 0000000..06a47fa --- /dev/null +++ b/Python Waveforms Test/WF_SDK/protocol/i2c.py @@ -0,0 +1,257 @@ +""" PROTOCOL: I2C CONTROL FUNCTIONS: open, read, write, exchange, spy, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators +import inspect # get caller information + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error, warning + +"""-----------------------------------------------------------------------""" + +def __check_warning__(device_data, nak): + """ + check for I2C errors + """ + if nak != 0: + raise warning("NAK: index " + str(nak), inspect.stack()[1].function, "protocol/i2c") + return + +"""-----------------------------------------------------------------------""" + +def open(device_data, sda, scl, clk_rate=100e03, stretching=True): + """ + initializes I2C communication + + parameters: - device data + - sda (DIO line used for data) + - scl (DIO line used for clock) + - rate (clock frequency in Hz, default is 100KHz) + - stretching (enables/disables clock stretching) + """ + # reset the interface + if dwf.FDwfDigitalI2cReset(device_data.handle) == 0: + check_error() + + # clock stretching + if stretching: + if dwf.FDwfDigitalI2cStretchSet(device_data.handle, ctypes.c_int(1)) == 0: + check_error() + else: + if dwf.FDwfDigitalI2cStretchSet(device_data.handle, ctypes.c_int(0)) == 0: + check_error() + + # set clock frequency + if dwf.FDwfDigitalI2cRateSet(device_data.handle, ctypes.c_double(clk_rate)) == 0: + check_error() + + # set communication lines + if dwf.FDwfDigitalI2cSclSet(device_data.handle, ctypes.c_int(scl)) == 0: + check_error() + if dwf.FDwfDigitalI2cSdaSet(device_data.handle, ctypes.c_int(sda)) == 0: + check_error() + + # check bus + nak = ctypes.c_int() + if dwf.FDwfDigitalI2cClear(device_data.handle, ctypes.byref(nak)) == 0: + check_error() + if nak.value == 0: + raise warning("I2C bus lockup", "open", "protocol/i2c") + + # write 0 bytes + if dwf.FDwfDigitalI2cWrite(device_data.handle, ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.byref(nak)) == 0: + check_error() + __check_warning__(device_data, nak) + return + +"""-----------------------------------------------------------------------""" + +def write(device_data, data, address): + """ + send data through I2C + + parameters: - device data + - data of type string, int, or list of characters/integers + - address (8-bit address of the slave device) + """ + # cast data + if type(data) == int: + data = "".join(chr(data)) + elif type(data) == list: + data = "".join(chr(element) for element in data) + + # encode the string into a string buffer + data = bytes(data, "utf-8") + buffer = (ctypes.c_ubyte * len(data))() + for index in range(0, len(buffer)): + buffer[index] = ctypes.c_ubyte(data[index]) + + # send + nak = ctypes.c_int() + if dwf.FDwfDigitalI2cWrite(device_data.handle, ctypes.c_int(address << 1), buffer, ctypes.c_int(ctypes.sizeof(buffer)), ctypes.byref(nak)) == 0: + check_error() + + # check for not acknowledged + __check_warning__(device_data, nak) + return "" + +"""-----------------------------------------------------------------------""" + +def read(device_data, count, address): + """ + receives data from I2C + + parameters: - device data + - count (number of bytes to receive) + - address (8-bit address of the slave device) + + return: - integer list containing the received bytes + """ + # create buffer to store data + buffer = (ctypes.c_ubyte * count)() + + # receive + nak = ctypes.c_int() + if dwf.FDwfDigitalI2cRead(device_data.handle, ctypes.c_int(address << 1), buffer, ctypes.c_int(count), ctypes.byref(nak)) == 0: + check_error() + + # decode data + data = [int(element) for element in buffer] + + # check for not acknowledged + __check_warning__(device_data, nak) + return data + +"""-----------------------------------------------------------------------""" + +def exchange(device_data, data, count, address): + """ + sends and receives data using the I2C interface + + parameters: - device data + - data of type string, int, or list of characters/integers + - count (number of bytes to receive) + - address (8-bit address of the slave device) + + return: - integer list containing the received bytes + """ + # create buffer to store data + buffer = (ctypes.c_ubyte * count)() + + # cast data + if type(data) == int: + data = "".join(chr(data)) + elif type(data) == list: + data = "".join(chr(element) for element in data) + + # encode the string into a string buffer + data = bytes(data, "utf-8") + tx_buffer = (ctypes.c_ubyte * len(data))() + for index in range(0, len(tx_buffer)): + tx_buffer[index] = ctypes.c_ubyte(data[index]) + + # send and receive + nak = ctypes.c_int() + if dwf.FDwfDigitalI2cWriteRead(device_data.handle, ctypes.c_int(address << 1), tx_buffer, ctypes.c_int(ctypes.sizeof(tx_buffer)), buffer, ctypes.c_int(count), ctypes.byref(nak)) == 0: + check_error() + + # decode data + rec_data = [int(element) for element in buffer] + + # check for not acknowledged + __check_warning__(device_data, nak) + return rec_data + +"""-----------------------------------------------------------------------""" + +def spy(device_data, count = 16): + pass + """ + receives data from I2C + + parameters: - device data + - count (number of bytes to receive), default is 16 + + return: - class containing the received data: start, address, direction, message, stop + - error message or empty string + """ + """# variable to store the errors + error = "" + + # variable to store the data + class message: + start = "" + address = 0 + direction = "" + data = [] + stop = "" + + # start the interfcae + if dwf.FDwfDigitalI2cSpyStart(device_data.handle) + + # read data + start = ctypes.c_int() + stop = ctypes.c_int() + data = (ctypes.c_ubyte * count)() + count = ctypes.c_int(count) + nak = ctypes.c_int() + if if dwf.FDwfDigitalI2cSpyStatus(device_data.handle, ctypes.byref(start), ctypes.byref(stop), ctypes.byref(data), ctypes.byref(count), ctypes.byref(nak)) == 0: + error = "Communication with the device failed." + + # decode data + if start.value != 0: + + # start condition + if start.value == 1: + message.start = "Start" + elif start.value == 2: + message.start = "Restart" + + # get address + message.address = hex(data[0] >> 1) + + # decide message direction + if data[0] & 1 == 0: + message.direction = "Write" + else: + message.direction = "Read" + + # get message + message.data = [int(element) for element in data] + + if stop.value != 0: + message.stop = "Stop" + + # check for not acknowledged + if nak.value != 0 and error == "": + error = "NAK: index " + str(nak.value) + + return message, error""" + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the i2c interface + """ + if dwf.FDwfDigitalI2cReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/protocol/spi.py b/Python Waveforms Test/WF_SDK/protocol/spi.py new file mode 100644 index 0000000..4f3a13b --- /dev/null +++ b/Python Waveforms Test/WF_SDK/protocol/spi.py @@ -0,0 +1,323 @@ +""" PROTOCOL: SPI CONTROL FUNCTIONS: open, read, write, exchange, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +def open(device_data, cs, sck, miso=None, mosi=None, clk_frequency=1e06, mode=0, order=True): + """ + initializes SPI communication + + parameters: - device data + - cs (DIO line used for chip select) + - sck (DIO line used for serial clock) + - miso (DIO line used for master in - slave out, optional) + - mosi (DIO line used for master out - slave in, optional) + - frequency (communication frequency in Hz, default is 1MHz) + - mode (SPI mode: 0: CPOL=0, CPHA=0; 1: CPOL-0, CPHA=1; 2: CPOL=1, CPHA=0; 3: CPOL=1, CPHA=1) + - order (endianness, True means MSB first - default, False means LSB first) + """ + # set the clock frequency + if dwf.FDwfDigitalSpiFrequencySet(device_data.handle, ctypes.c_double(clk_frequency)) == 0: + check_error() + + # set the clock pin + if dwf.FDwfDigitalSpiClockSet(device_data.handle, ctypes.c_int(sck)) == 0: + check_error() + + if mosi != None: + # set the mosi pin + if dwf.FDwfDigitalSpiDataSet(device_data.handle, ctypes.c_int(0), ctypes.c_int(mosi)) == 0: + check_error() + + # set the initial state + if dwf.FDwfDigitalSpiIdleSet(device_data.handle, ctypes.c_int(0), constants.DwfDigitalOutIdleZet) == 0: + check_error() + + if miso != None: + # set the miso pin + if dwf.FDwfDigitalSpiDataSet(device_data.handle, ctypes.c_int(1), ctypes.c_int(miso)) == 0: + check_error() + + # set the initial state + if dwf.FDwfDigitalSpiIdleSet(device_data.handle, ctypes.c_int(1), constants.DwfDigitalOutIdleZet) == 0: + check_error() + + # set the SPI mode + if dwf.FDwfDigitalSpiModeSet(device_data.handle, ctypes.c_int(mode)) == 0: + check_error() + + # set endianness + if order: + # MSB first + if dwf.FDwfDigitalSpiOrderSet(device_data.handle, ctypes.c_int(1)) == 0: + check_error() + else: + # LSB first + if dwf.FDwfDigitalSpiOrderSet(device_data.handle, ctypes.c_int(0)) == 0: + check_error() + + # set the cs pin HIGH + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(1)) == 0: + check_error() + + # dummy write + if dwf.FDwfDigitalSpiWriteOne(device_data.handle, ctypes.c_int(1), ctypes.c_int(0), ctypes.c_int(0)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def read(device_data, count, cs): + """ + receives data from SPI + + parameters: - device data + - count (number of bytes to receive) + - chip select line number + + return: - integer list containing the received bytes + """ + # enable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(0)) == 0: + check_error() + + # create buffer to store data + buffer = (ctypes.c_ubyte*count)() + + # read array of 8 bit elements + if dwf.FDwfDigitalSpiRead(device_data.handle, ctypes.c_int(1), ctypes.c_int(8), buffer, ctypes.c_int(len(buffer))) == 0: + check_error() + + # disable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(1)) == 0: + check_error() + + # decode data + data = [int(element) for element in buffer] + + return data + +"""-----------------------------------------------------------------------""" + +def write(device_data, data, cs): + """ + send data through SPI + + parameters: - device data + - data of type string, int, or list of characters/integers + - chip select line number + """ + # cast data + if type(data) == int: + data = "".join(chr(data)) + elif type(data) == list: + data = "".join(chr(element) for element in data) + + # enable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(0)) == 0: + check_error() + + # create buffer to write + data = bytes(data, "utf-8") + buffer = (ctypes.c_ubyte * len(data))() + for index in range(0, len(buffer)): + buffer[index] = ctypes.c_ubyte(data[index]) + + # write array of 8 bit elements + if dwf.FDwfDigitalSpiWrite(device_data.handle, ctypes.c_int(1), ctypes.c_int(8), buffer, ctypes.c_int(len(buffer))) == 0: + check_error() + + # disable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(1)) == 0: + check_error() + + return + +"""-----------------------------------------------------------------------""" + +def exchange(device_data, data, count, cs): + """ + sends and receives data using the SPI interface + + parameters: - device data + - data of type string, int, or list of characters/integers + - count (number of bytes to receive) + - chip select line number + + return: - integer list containing the received bytes + """ + # cast data + if type(data) == int: + data = "".join(chr(data)) + elif type(data) == list: + data = "".join(chr(element) for element in data) + + # enable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(0)) == 0: + check_error() + + # create buffer to write + data = bytes(data, "utf-8") + tx_buffer = (ctypes.c_ubyte * len(data))() + for index in range(0, len(tx_buffer)): + tx_buffer[index] = ctypes.c_ubyte(data[index]) + + # create buffer to store data + rx_buffer = (ctypes.c_ubyte*count)() + + # write to MOSI and read from MISO + if dwf.FDwfDigitalSpiWriteRead(device_data.handle, ctypes.c_int(1), ctypes.c_int(8), tx_buffer, ctypes.c_int(len(tx_buffer)), rx_buffer, ctypes.c_int(len(rx_buffer))) == 0: + check_error() + + # disable the chip select line + if dwf.FDwfDigitalSpiSelect(device_data.handle, ctypes.c_int(cs), ctypes.c_int(1)) == 0: + check_error() + + # decode data + data = [int(element) for element in rx_buffer] + + return data + +"""-----------------------------------------------------------------------""" + +def spy(device_data, count, cs, sck, mosi=None, miso=None, word_size=8): + """ + receives data from SPI + + parameters: - device data + - count (number of bytes to receive) + - chip select line number + - serial clock line number + - master out - slave in - optional + - master in - slave out - optional + - word size in bits (default is 8) + + returns: - class containing the received data: mosi, miso + - error message or empty string + """ + pass + """# variable to store errors + error = "" + + # create static data structure + class message: + mosi = 0 + miso = 0 + + # record mode + if dwf.FDwfDigitalInAcquisitionModeSet(device_data.handle, constants.acqmodeRecord) + + # for sync mode set divider to -1 + if dwf.FDwfDigitalInDividerSet(device_data.handle, ctypes.c_int(-1)) + + # 8 bit per sample format, DIO 0-7 + if dwf.FDwfDigitalInSampleFormatSet(device_data.handle, ctypes.c_int(8)) + + # continuous sampling + if dwf.FDwfDigitalInTriggerPositionSet(device_data.handle, ctypes.c_int(-1)) + + # in sync mode the trigger is used for sampling condition + # trigger detector mask: low & high & (rising | falling) + if dwf.FDwfDigitalInTriggerSet(device_data.handle, ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int((1 << sck) | (1 << cs)), ctypes.c_int(0)) + # sample on clock rising edge for sampling bits, or CS rising edge to detect frames + + # start detection + if dwf.FDwfDigitalInConfigure(device_data.handle, ctypes.c_bool(0), ctypes.c_bool(1)) + + # fill buffer + status = ctypes.c_byte() + available = ctypes.c_int() + lost = ctypes.c_int() + corrupted = ctypes.c_int() + if dwf.FDwfDigitalInStatus(device_data.handle, ctypes.c_int(1), ctypes.byref(status)) + if dwf.FDwfDigitalInStatusRecord(device_data.handle, ctypes.byref(available), ctypes.byref(lost), ctypes.byref(corrupted)) + + # check data integrity + if lost.value : + error = "Samples were lost" + if corrupted.value : + error = "Samples could be corrupted" + + # limit data size + if available.value > count : + available = ctypes.c_int(count) + + # load data from internal buffer + data = (ctypes.c_uint8 * available)() + if dwf.FDwfDigitalInStatusData(device_data.handle, data, available) + + # get message + bit_count = 0 + for index in range(available.value): + + # CS low, active + if (data[index] >> cs) & 1: + # log leftover bits, frame not multiple of bit count + if bit_count != 0: + # convert data + message.mosi = chr(message.mosi) + message.miso = chr(message.miso) + + return message, error + + # CS low, active + else: + bit_count += 1 # increment bit count + + message.mosi <<= 1 # shift existing bits + message.miso <<= 1 + + # check outgoing data + if (data[index] >> mosi) & 1: + message.mosi |= 1 + + # check incoming data + if (data[index] >> miso) & 1: + message.miso |= 1 + + # got nBits of bits + if bit_count >= word_size: + # convert data + message.mosi = chr(message.mosi) + message.miso = chr(message.miso) + + return message, error + + # convert data + message.mosi = chr(message.mosi) + message.miso = chr(message.miso) + + return message, error""" + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the spi interface + """ + if dwf.FDwfDigitalSpiReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/protocol/uart.py b/Python Waveforms Test/WF_SDK/protocol/uart.py new file mode 100644 index 0000000..96d7856 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/protocol/uart.py @@ -0,0 +1,169 @@ +""" PROTOCOL: UART CONTROL FUNCTIONS: open, read, write, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error, warning + +"""-----------------------------------------------------------------------""" + +def open(device_data, rx, tx, baud_rate=9600, parity=None, data_bits=8, stop_bits=1): + """ + initializes UART communication + + parameters: - device data + - rx (DIO line used to receive data) + - tx (DIO line used to send data) + - baud_rate (communication speed, default is 9600 bits/s) + - parity possible: None (default), True means even, False means odd + - data_bits (default is 8) + - stop_bits (default is 1) + """ + # set baud rate + if dwf.FDwfDigitalUartRateSet(device_data.handle, ctypes.c_double(baud_rate)) == 0: + check_error() + + # set communication channels + if dwf.FDwfDigitalUartTxSet(device_data.handle, ctypes.c_int(tx)) == 0: + check_error() + if dwf.FDwfDigitalUartRxSet(device_data.handle, ctypes.c_int(rx)) == 0: + check_error() + + # set data bit count + if dwf.FDwfDigitalUartBitsSet(device_data.handle, ctypes.c_int(data_bits)) == 0: + check_error() + + # set parity bit requirements + if parity == True: + parity = 2 + elif parity == False: + parity = 1 + else: + parity = 0 + if dwf.FDwfDigitalUartParitySet(device_data.handle, ctypes.c_int(parity)) == 0: + check_error() + + # set stop bit count + if dwf.FDwfDigitalUartStopSet(device_data.handle, ctypes.c_double(stop_bits)) == 0: + check_error() + + # initialize channels with idle levels + + # dummy read + dummy_buffer = ctypes.create_string_buffer(0) + dummy_buffer = ctypes.c_int(0) + dummy_parity_flag = ctypes.c_int(0) + if dwf.FDwfDigitalUartRx(device_data.handle, dummy_buffer, ctypes.c_int(0), ctypes.byref(dummy_buffer), ctypes.byref(dummy_parity_flag)) == 0: + check_error() + + # dummy write + if dwf.FDwfDigitalUartTx(device_data.handle, dummy_buffer, ctypes.c_int(0)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def read(device_data): + """ + receives data from UART + + parameters: - device data + + return: - integer list containing the received bytes + """ + # variable to store results + rx_data = [] + + # create empty string buffer + data = (ctypes.c_ubyte * 8193)() + + # character counter + count = ctypes.c_int(0) + + # parity flag + parity_flag= ctypes.c_int(0) + + # read up to 8k characters + if dwf.FDwfDigitalUartRx(device_data.handle, data, ctypes.c_int(ctypes.sizeof(data)-1), ctypes.byref(count), ctypes.byref(parity_flag)) == 0: + check_error() + + # append current data chunks + for index in range(0, count.value): + rx_data.append(int(data[index])) + + # ensure data integrity + while count.value > 0: + # create empty string buffer + data = (ctypes.c_ubyte * 8193)() + + # character counter + count = ctypes.c_int(0) + + # parity flag + parity_flag= ctypes.c_int(0) + + # read up to 8k characters + if dwf.FDwfDigitalUartRx(device_data.handle, data, ctypes.c_int(ctypes.sizeof(data)-1), ctypes.byref(count), ctypes.byref(parity_flag)) == 0: + check_error() + # append current data chunks + for index in range(0, count.value): + rx_data.append(int(data[index])) + + # check for not acknowledged + if parity_flag.value < 0: + raise warning("Buffer overflow", "read", "protocol/uart") + elif parity_flag.value > 0: + raise warning("Parity error: index {}".format(parity_flag.value), "read", "protocol/uart") + return rx_data + +"""-----------------------------------------------------------------------""" + +def write(device_data, data): + """ + send data through UART + + parameters: - data of type string, int, or list of characters/integers + """ + # cast data + if type(data) == int: + data = "".join(chr(data)) + elif type(data) == list: + data = "".join(chr(element) for element in data) + + # encode the string into a string buffer + data = ctypes.create_string_buffer(data.encode("UTF-8")) + + # send text, trim zero ending + if dwf.FDwfDigitalUartTx(device_data.handle, data, ctypes.c_int(ctypes.sizeof(data)-1)) == 0: + check_error() + + return + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the uart interface + """ + if dwf.FDwfDigitalUartReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/scope.py b/Python Waveforms Test/WF_SDK/scope.py new file mode 100644 index 0000000..f0cf1fc --- /dev/null +++ b/Python Waveforms Test/WF_SDK/scope.py @@ -0,0 +1,211 @@ +""" OSCILLOSCOPE CONTROL FUNCTIONS: open, measure, trigger, record, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class data: + """ stores the sampling frequency and the buffer size """ + sampling_frequency = 20e06 + buffer_size = 8192 + max_buffer_size = 0 + +"""-----------------------------------------------------------------------""" + +class trigger_source: + """ trigger source names """ + none = constants.trigsrcNone + analog = constants.trigsrcDetectorAnalogIn + digital = constants.trigsrcDetectorDigitalIn + external = [None, constants.trigsrcExternal1, constants.trigsrcExternal2, constants.trigsrcExternal3, constants.trigsrcExternal4] + +"""-----------------------------------------------------------------------""" + +def open(device_data, sampling_frequency=20e06, buffer_size=0, offset=0, amplitude_range=5): + """ + initialize the oscilloscope + + parameters: - device data + - sampling frequency in Hz, default is 20MHz + - buffer size, default is 0 (maximum) + - offset voltage in Volts, default is 0V + - amplitude range in Volts, default is ±5V + """ + # set global variables + data.sampling_frequency = sampling_frequency + data.max_buffer_size = device_data.analog.input.max_buffer_size + + # enable all channels + if dwf.FDwfAnalogInChannelEnableSet(device_data.handle, ctypes.c_int(-1), ctypes.c_bool(True)) == 0: + check_error() + + # set offset voltage (in Volts) + if dwf.FDwfAnalogInChannelOffsetSet(device_data.handle, ctypes.c_int(-1), ctypes.c_double(offset)) == 0: + check_error() + + # set range (maximum signal amplitude in Volts) + if dwf.FDwfAnalogInChannelRangeSet(device_data.handle, ctypes.c_int(-1), ctypes.c_double(amplitude_range)) == 0: + check_error() + + # set the buffer size (data point in a recording) + if buffer_size == 0: + buffer_size = data.max_buffer_size + data.buffer_size = buffer_size + if dwf.FDwfAnalogInBufferSizeSet(device_data.handle, ctypes.c_int(buffer_size)) == 0: + check_error() + + # set the acquisition frequency (in Hz) + if dwf.FDwfAnalogInFrequencySet(device_data.handle, ctypes.c_double(sampling_frequency)) == 0: + check_error() + + # disable averaging (for more info check the documentation) + if dwf.FDwfAnalogInChannelFilterSet(device_data.handle, ctypes.c_int(-1), constants.filterDecimate) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def measure(device_data, channel): + """ + measure a voltage + + parameters: - device data + - the selected oscilloscope channel (1-2, or 1-4) + + returns: - the measured voltage in Volts + """ + # set up the instrument + if dwf.FDwfAnalogInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(False)) == 0: + check_error() + + # read data to an internal buffer + if dwf.FDwfAnalogInStatus(device_data.handle, ctypes.c_bool(False), ctypes.c_int(0)) == 0: + check_error() + + # extract data from that buffer + voltage = ctypes.c_double() # variable to store the measured voltage + if dwf.FDwfAnalogInStatusSample(device_data.handle, ctypes.c_int(channel - 1), ctypes.byref(voltage)) == 0: + check_error() + + # store the result as float + voltage = voltage.value + return voltage + +"""-----------------------------------------------------------------------""" + +def trigger(device_data, enable, source=trigger_source.none, channel=1, timeout=0, edge_rising=True, level=0): + """ + set up triggering + + parameters: - device data + - enable / disable triggering with True/False + - trigger source - possible: none, analog, digital, external[1-4] + - trigger channel - possible options: 1-4 for analog, or 0-15 for digital + - auto trigger timeout in seconds, default is 0 + - trigger edge rising - True means rising, False means falling, default is rising + - trigger level in Volts, default is 0V + """ + if enable and source != constants.trigsrcNone: + # enable/disable auto triggering + if dwf.FDwfAnalogInTriggerAutoTimeoutSet(device_data.handle, ctypes.c_double(timeout)) == 0: + check_error() + + # set trigger source + if dwf.FDwfAnalogInTriggerSourceSet(device_data.handle, source) == 0: + check_error() + + # set trigger channel + if source == constants.trigsrcDetectorAnalogIn: + channel -= 1 # decrement analog channel index + if dwf.FDwfAnalogInTriggerChannelSet(device_data.handle, ctypes.c_int(channel)) == 0: + check_error() + + # set trigger type + if dwf.FDwfAnalogInTriggerTypeSet(device_data.handle, constants.trigtypeEdge) == 0: + check_error() + + # set trigger level + if dwf.FDwfAnalogInTriggerLevelSet(device_data.handle, ctypes.c_double(level)) == 0: + check_error() + + # set trigger edge + if edge_rising: + # rising edge + if dwf.FDwfAnalogInTriggerConditionSet(device_data.handle, constants.trigcondRisingPositive) == 0: + check_error() + else: + # falling edge + if dwf.FDwfAnalogInTriggerConditionSet(device_data.handle, constants.trigcondFallingNegative) == 0: + check_error() + else: + # turn off the trigger + if dwf.FDwfAnalogInTriggerSourceSet(device_data.handle, constants.trigsrcNone) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def record(device_data, channel): + """ + record an analog signal + + parameters: - device data + - the selected oscilloscope channel (1-2, or 1-4) + + returns: - a list with the recorded voltages + """ + # set up the instrument + if dwf.FDwfAnalogInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(True)) == 0: + check_error() + + # read data to an internal buffer + while True: + status = ctypes.c_byte() # variable to store buffer status + if dwf.FDwfAnalogInStatus(device_data.handle, ctypes.c_bool(True), ctypes.byref(status)) == 0: + check_error() + + # check internal buffer status + if status.value == constants.DwfStateDone.value: + # exit loop when ready + break + + # copy buffer + buffer = (ctypes.c_double * data.buffer_size)() # create an empty buffer + if dwf.FDwfAnalogInStatusData(device_data.handle, ctypes.c_int(channel - 1), buffer, ctypes.c_int(data.buffer_size)) == 0: + check_error() + + # convert into list + buffer = [float(element) for element in buffer] + return buffer + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the scope + """ + if dwf.FDwfAnalogInReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/static.py b/Python Waveforms Test/WF_SDK/static.py new file mode 100644 index 0000000..474ad72 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/static.py @@ -0,0 +1,266 @@ +""" STATIC I/O CONTROL FUNCTIONS: set_mode, get_state, set_state, set_current, set_pull, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class data: + """ stores the state of the instrument """ + channel = -1 + count = 0 + class nodes : + current = -1 + pull_enable = -1 + pull_direction = -1 + pull_weak = -1 + +"""-----------------------------------------------------------------------""" + +class pull: + """ digital pin pull directions """ + down = 0 + idle = 0.5 + up = 1 + +"""-----------------------------------------------------------------------""" + +def set_mode(device_data, channel, output): + """ + set a DIO line as input, or as output + + parameters: - device data + - selected DIO channel number + - True means output, False means input + """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + + # count the DIO channels + global data + data.count = min(device_data.digital.input.channel_count, device_data.digital.output.channel_count) + + # load current state of the output enable buffer + mask = ctypes.c_uint16() + if dwf.FDwfDigitalIOOutputEnableGet(device_data.handle, ctypes.byref(mask)) == 0: + check_error() + mask = mask.value + + # set bit in mask + if output == True: + mask |= __rotate_left__(1, channel, data.count) + else: + bits = pow(2, data.count) - 2 + mask &= __rotate_left__(bits, channel, data.count) + + # set the pin to output + if dwf.FDwfDigitalIOOutputEnableSet(device_data.handle, ctypes.c_int(mask)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def get_state(device_data, channel): + """ + get the state of a DIO line + + parameters: - device data + - selected DIO channel number + + returns: - True if the channel is HIGH, or False, if the channel is LOW + """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + + # load internal buffer with current state of the pins + if dwf.FDwfDigitalIOStatus(device_data.handle) == 0: + check_error() + + # get the current state of the pins + data = ctypes.c_uint32() # variable for this current state + if dwf.FDwfDigitalIOInputStatus(device_data.handle, ctypes.byref(data)) == 0: + check_error() + data = data.value + + # check the required bit + if data & (1 << channel) != 0: + value = True + else: + value = False + return value + +"""-----------------------------------------------------------------------""" + +def set_state(device_data, channel, value): + """ + set a DIO line as input, or as output + + parameters: - device data + - selected DIO channel number + - True means HIGH, False means LOW + """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + + # count the DIO channels + global data + data.count = min(device_data.digital.input.channel_count, device_data.digital.output.channel_count) + + # load current state of the output state buffer + mask = ctypes.c_uint16() + if dwf.FDwfDigitalIOOutputGet(device_data.handle, ctypes.byref(mask)) == 0: + check_error() + + # set bit in mask + if value == True: + mask |= __rotate_left__(1, channel, data.count) + else: + bits = pow(2, data.count) - 2 + mask &= __rotate_left__(bits, channel, data.count) + + # set the pin state + if dwf.FDwfDigitalIOOutputSet(device_data.handle, ctypes.c_int(mask)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def set_current(device_data, current): + """ + limit the output current of the DIO lines + + parameters: - device data + - current limit in mA: possible values are 2, 4, 6, 8, 12 and 16mA + """ + # search for the digital voltage channel + global data + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "VDD": + data.channel = channel_index + break + + # search for the drive node + if data.channel >= 0: + for node_index in range(device_data.analog.IO.node_count[data.channel]): + if device_data.analog.IO.node_name[data.channel][node_index] == "Drive": + data.nodes.current = node_index + break + + # set limit + if data.channel >= 0 and data.nodes.current >= 0: + current = max(min(current, device_data.analog.IO.max_set_range[data.channel][data.nodes.current]), device_data.analog.IO.min_set_range[data.channel][data.nodes.current]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, data.channel, data.nodes.current, ctypes.c_double(current)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def set_pull(device_data, channel, direction): + """ + pull a DIO line up, or down + + parameters: - device data + - selected DIO channel number + - direction: pull.up, pull.idle, or pull.down + """ + if device_data.name == "Digital Discovery": + channel = channel - 24 + + # count the DIO channels + global data + data.count = min(device_data.digital.input.channel_count, device_data.digital.output.channel_count) + + # search for the digital voltage channel + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "VDD": + data.channel = channel_index + break + + # search for the pull enable node + if data.channel >= 0: + for node_index in range(device_data.analog.IO.node_count[data.channel]): + if device_data.analog.IO.node_name[data.channel][node_index] == "DIOPE": + data.nodes.pull_enable = node_index + break + + # search for the pull direction node + if data.channel >= 0: + for node_index in range(device_data.analog.IO.node_count[data.channel]): + if device_data.analog.IO.node_name[data.channel][node_index] == "DIOPP": + data.nodes.pull_direction = node_index + break + + # search for the weak pull node + if data.channel >= 0: + for node_index in range(device_data.analog.IO.node_count[data.channel]): + if device_data.analog.IO.node_name[data.channel][node_index] == "DINPP": + data.nodes.pull_weak = node_index + break + + # set pull enable mask + mask = ctypes.c_uint16() + if dwf.FDwfAnalogIOChannelNodeGet(device_data.handle, data.channel, data.nodes.pull_enable, ctypes.byref(mask)) == 0: + check_error() + bitmask = int(mask) + if direction == pull.idle: + bitmask |= __rotate_left__(1, channel, data.count) + else: + bits = int(pow(2, data.count) - 2) + bitmask &= __rotate_left__(bits, channel, data.count) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, data.channel, data.nodes.pull_enable, bitmask) == 0: + check_error() + + # set direction if necessary + if direction != pull.idle: + # set direction mask + mask = ctypes.c_uint16() + if dwf.FDwfAnalogIOChannelNodeGet(device_data.handle, data.channel, data.nodes.pull_direction, ctypes.byref(mask)) == 0: + check_error() + bitmask = int(mask) + if direction == pull.up: + bitmask |= __rotate_left__(1, channel, data.count) + else: + bits = int(pow(2, data.count) - 2) + bitmask &= __rotate_left__(bits, channel, data.count) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, data.channel, data.nodes.pull_direction, bitmask) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the instrument + """ + if dwf.FDwfDigitalIOReset(device_data.handle) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def __rotate_left__(number, position, size=16): + """ + rotate left a number bitwise + """ + return (number << position) | (number >> (size - position)) diff --git a/Python Waveforms Test/WF_SDK/supplies.py b/Python Waveforms Test/WF_SDK/supplies.py new file mode 100644 index 0000000..21f8b11 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/supplies.py @@ -0,0 +1,221 @@ +""" POWER SUPPLIES CONTROL FUNCTIONS: switch, switch_fixed, switch_variable, switch_digital, close """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class data: + """ power supply parameters """ + master_state = False # master switch + positive_state = False # positive supply switch + negative_state = False # negative supply switch + state = False # digital/6V supply state + positive_voltage = 0 # positive supply voltage + negative_voltage = 0 # negative supply voltage + voltage = 0 # digital/6V supply voltage + positive_current = 0 # positive supply current + negative_current = 0 # negative supply current + current = 0 # digital/6V supply current + +"""-----------------------------------------------------------------------""" + +def switch(device_data, supplies_data): + """ + turn the power supplies on/off + + parameters: - device data + - class containing supplies data: + - master_state + - state and/or positive_state and negative_state + - voltage and/or positive_voltage and negative_voltage + - current and/or positive_current and negative_current + """ + # find the positive supply + channel = -1 + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "V+" or device_data.analog.IO.channel_label[channel_index] == "p25V": + channel = channel_index + break + if channel != -1: + # set enable + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Enable": + node = node_index + break + if node != -1: + enable = ctypes.c_int(supplies_data.positive_state) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), enable) == 0: + check_error() + except: + pass + # set voltage + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Voltage": + node = node_index + break + if node != -1: + voltage = min(max(supplies_data.positive_voltage, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(voltage)) == 0: + check_error() + except: + pass + # set current + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Current": + node = node_index + break + if node != -1: + current = min(max(supplies_data.positive_current, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(current)) == 0: + check_error() + except: + pass + + # find the negative supply + channel = -1 + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "V-" or device_data.analog.IO.channel_label[channel_index] == "n25V": + channel = channel_index + break + if channel != -1: + # set enable + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Enable": + node = node_index + break + if node != -1: + enable = ctypes.c_int(supplies_data.negative_state) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), enable) == 0: + check_error() + except: + pass + # set voltage + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Voltage": + node = node_index + break + if node != -1: + voltage = min(max(supplies_data.negative_voltage, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(voltage)) == 0: + check_error() + except: + pass + # set current + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Current": + node = node_index + break + if node != -1: + current = min(max(supplies_data.negative_current, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(current)) == 0: + check_error() + except: + pass + + # find the digital/6V supply + channel = -1 + for channel_index in range(device_data.analog.IO.channel_count): + if device_data.analog.IO.channel_label[channel_index] == "VDD" or device_data.analog.IO.channel_label[channel_index] == "p6V": + channel = channel_index + break + if channel != -1: + # set enable + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Enable": + node = node_index + break + if node != -1: + enable = ctypes.c_int(supplies_data.state) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), enable) == 0: + check_error() + except: + pass + # set voltage + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Voltage": + node = node_index + break + if node != -1: + voltage = min(max(supplies_data.voltage, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(voltage)) == 0: + check_error() + except: + pass + # set current + try: + node = -1 + # find the voltage node + for node_index in range(device_data.analog.IO.node_count[channel]): + if device_data.analog.IO.node_name[channel][node_index] == "Current": + node = node_index + break + if node != -1: + current = min(max(supplies_data.current, device_data.analog.IO.min_set_range[channel][node]), device_data.analog.IO.max_set_range[channel][node]) + if dwf.FDwfAnalogIOChannelNodeSet(device_data.handle, ctypes.c_int(channel), ctypes.c_int(node), ctypes.c_double(current)) == 0: + check_error() + except: + pass + + # turn all supplies on/off + try: + if dwf.FDwfAnalogIOEnableSet(device_data.handle, ctypes.c_int(supplies_data.master_state)) == 0: + check_error() + except: + pass + return + +"""-----------------------------------------------------------------------""" + +def close(device_data): + """ + reset the supplies + """ + if dwf.FDwfAnalogIOReset(device_data.handle) == 0: + check_error() + return diff --git a/Python Waveforms Test/WF_SDK/tools.py b/Python Waveforms Test/WF_SDK/tools.py new file mode 100644 index 0000000..cf6d705 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/tools.py @@ -0,0 +1,71 @@ +""" TOOLS: spectrum """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators +from math import log10, sqrt # import necessary math functions + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants + +"""-----------------------------------------------------------------------""" + +class window: + """ FFT windows """ + rectangular = constants.DwfWindowRectangular + triangular = constants.DwfWindowTriangular + hamming = constants.DwfWindowHamming + hann = constants.DwfWindowHann + cosine = constants.DwfWindowCosine + blackman_harris = constants.DwfWindowBlackmanHarris + flat_top = constants.DwfWindowFlatTop + kaiser = constants.DwfWindowKaiser + +"""-----------------------------------------------------------------------""" + +def spectrum(buffer, window, sample_rate, frequency_start, frequency_stop): + """ + calculates the spectrum of a signal + + parameters: - buffer: list of data points in the signal + - window type: rectangular, triangular, hamming, hann, cosine, blackman_harris, flat_top, kaiser + - sample rate of the signal in Hz + - starting frequency of the spectrum in Hz + - end frequency of the spectrum in Hz + """ + # get and apply window + buffer_length = len(buffer) + window_buffer = (ctypes.c_double * buffer_length)() # create an empty buffer + dwf.FDwfSpectrumWindow(window_buffer, ctypes.c_int(buffer_length), window, ctypes.c_double(1), ctypes.c_double(0)) + for index in range(buffer_length): + buffer[index] *= float(window_buffer[index]) + + # get the spectrum + spectrum_length = int(buffer_length / 2 + 1) + c_spectrum = (ctypes.c_double * spectrum_length)() # create an empty buffer + c_buffer = (ctypes.c_double * buffer_length)() + for index in range(0, len(buffer)): + c_buffer[index] = ctypes.c_double(buffer[index]) + frequency_start = max(frequency_start * 2.0 / sample_rate, 0.0) + frequency_stop = min(frequency_stop * 2.0 / sample_rate, 1.0) + dwf.FDwfSpectrumTransform(c_buffer, ctypes.c_int(buffer_length), c_spectrum, ctypes.c_int(0), ctypes.c_int(spectrum_length), ctypes.c_double(frequency_start), ctypes.c_double(frequency_stop)) + spectrum = [] + for index in range(spectrum_length): + spectrum.append(20.0 * log10(float(c_spectrum[index]) / sqrt(2))) + return spectrum diff --git a/Python Waveforms Test/WF_SDK/wavegen.py b/Python Waveforms Test/WF_SDK/wavegen.py new file mode 100644 index 0000000..b765738 --- /dev/null +++ b/Python Waveforms Test/WF_SDK/wavegen.py @@ -0,0 +1,139 @@ +""" WAVEFORM GENERATOR CONTROL FUNCTIONS: generate, close, enable, disable """ + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants +from WF_SDK.device import check_error + +"""-----------------------------------------------------------------------""" + +class function: + """ function names """ + custom = constants.funcCustom + sine = constants.funcSine + square = constants.funcSquare + triangle = constants.funcTriangle + noise = constants.funcNoise + dc = constants.funcDC + pulse = constants.funcPulse + trapezium = constants.funcTrapezium + sine_power = constants.funcSinePower + ramp_up = constants.funcRampUp + ramp_down = constants.funcRampDown + +"""-----------------------------------------------------------------------""" + +def generate(device_data, channel, function, offset, frequency=1e03, amplitude=1, symmetry=50, wait=0, run_time=0, repeat=0, data=[]): + """ + generate an analog signal + + parameters: - device data + - the selected wavegen channel (1-2) + - function - possible: custom, sine, square, triangle, noise, ds, pulse, trapezium, sine_power, ramp_up, ramp_down + - offset voltage in Volts + - frequency in Hz, default is 1KHz + - amplitude in Volts, default is 1V + - signal symmetry in percentage, default is 50% + - wait time in seconds, default is 0s + - run time in seconds, default is infinite (0) + - repeat count, default is infinite (0) + - data - list of voltages, used only if function=custom, default is empty + """ + # enable channel + channel = ctypes.c_int(channel - 1) + if dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_bool(True)) == 0: + check_error() + + # set function type + if dwf.FDwfAnalogOutNodeFunctionSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, function) == 0: + check_error() + + # load data if the function type is custom + if function == constants.funcCustom: + data_length = len(data) + buffer = (ctypes.c_double * data_length)() + for index in range(0, len(buffer)): + buffer[index] = ctypes.c_double(data[index]) + if dwf.FDwfAnalogOutNodeDataSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, buffer, ctypes.c_int(data_length)) == 0: + check_error() + + # set frequency + if dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(frequency)) == 0: + check_error() + + # set amplitude or DC voltage + if dwf.FDwfAnalogOutNodeAmplitudeSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(amplitude)) == 0: + check_error() + + # set offset + if dwf.FDwfAnalogOutNodeOffsetSet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(offset)) == 0: + check_error() + + # set symmetry + if dwf.FDwfAnalogOutNodeSymmetrySet(device_data.handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(symmetry)) == 0: + check_error() + + # set running time limit + if dwf.FDwfAnalogOutRunSet(device_data.handle, channel, ctypes.c_double(run_time)) == 0: + check_error() + + # set wait time before start + if dwf.FDwfAnalogOutWaitSet(device_data.handle, channel, ctypes.c_double(wait)) == 0: + check_error() + + # set number of repeating cycles + if dwf.FDwfAnalogOutRepeatSet(device_data.handle, channel, ctypes.c_int(repeat)) == 0: + check_error() + + # start + if dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(True)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def close(device_data, channel=0): + """ + reset a wavegen channel, or all channels (channel=0) + """ + channel = ctypes.c_int(channel - 1) + if dwf.FDwfAnalogOutReset(device_data.handle, channel) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def enable(device_data, channel): + """ enables an analog output channel """ + channel = ctypes.c_int(channel - 1) + if dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(True)) == 0: + check_error() + return + +"""-----------------------------------------------------------------------""" + +def disable(device_data, channel): + """ disables an analog output channel """ + channel = ctypes.c_int(channel - 1) + if dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(False)) == 0: + check_error() + return diff --git a/Python Waveforms Test/dwfconstants.py b/Python Waveforms Test/dwfconstants.py index f1355d1..bcca305 100644 --- a/Python Waveforms Test/dwfconstants.py +++ b/Python Waveforms Test/dwfconstants.py @@ -250,4 +250,6 @@ enumfilterDiscovery2 = c_int(3) enumfilterDDiscovery = c_int(4) - +#uhhhhhhh +#DwfWindowRectangular = c_int(0) +#DwfWindowTriangular = c_int(1) diff --git a/Python Waveforms Test/osc_waveform_test.py b/Python Waveforms Test/osc_waveform_test.py new file mode 100644 index 0000000..724bc54 --- /dev/null +++ b/Python Waveforms Test/osc_waveform_test.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 27 13:53:15 2022 + +@author: Seth +""" + +import ctypes # import the C compatible data types +from sys import platform, path # this is needed to check the OS type and get the PATH +from os import sep # OS specific file path separators + +# load the dynamic library, get constants path (the path is OS specific) +if platform.startswith("win"): + # on Windows + dwf = ctypes.cdll.dwf + constants_path = "C:" + sep + "Program Files (x86)" + sep + "Digilent" + sep + "WaveFormsSDK" + sep + "samples" + sep + "py" +elif platform.startswith("darwin"): + # on macOS + lib_path = sep + "Library" + sep + "Frameworks" + sep + "dwf.framework" + sep + "dwf" + dwf = ctypes.cdll.LoadLibrary(lib_path) + constants_path = sep + "Applications" + sep + "WaveForms.app" + sep + "Contents" + sep + "Resources" + sep + "SDK" + sep + "samples" + sep + "py" +else: + # on Linux + dwf = ctypes.cdll.LoadLibrary("libdwf.so") + constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py" + +# import constants +path.append(constants_path) +import dwfconstants as constants + +########################################################################################### + +from WF_SDK import device, scope, wavegen # import instruments + +import matplotlib.pyplot as plt # needed for plotting + +"""-----------------------------------------------------------------------""" + +# connect to the device +device_data = device.open() + +"""-----------------------------------""" + +# initialize the scope with default settings +scope.open(device_data) + +# generate a 10KHz sine signal with 2V amplitude on channel 1 +wavegen.generate(device_data, channel=1, function=wavegen.function.sine, offset=0, frequency=10e03, amplitude=2) + +# record data with the scopeon channel 1 +buffer, time = scope.record(device_data, channel=1) + +# plot +time = [moment * 1e03 for moment in time] # convert time to ms +plt.plot(time, buffer) +plt.xlabel("time [ms]") +plt.ylabel("voltage [V]") +plt.show() + +# reset the scope +scope.close(device_data) + +# reset the wavegen +wavegen.close(device_data) + +"""-----------------------------------""" + +# close the connection +device.close(device_data) \ No newline at end of file