Hi Everyone,
I have a question regarding Analog Discovery 2.
I am currently developing a system that uses Analog Discovery 2 to control the frequency of an ultrasonic transducer and keep its vibration amplitude constant.
Specific objectives and implementation details are as follows:
The following process is based on the “Getting Started with WaveForms SDK” website.
1. Drive an ultrasonic transducer with a resonant frequency of 28.24kHz at approximately 28.3kHz using the generate() function.
2. Measure the output voltage of the ultrasonic transducer's pickup sensor using the AD2 oscilloscope function at a sampling frequency of 100MHz for 28µs, and calculate the peak amplitude Vmax using the record() function.
3. Calculate the difference e(t) between the target sensor voltage value Vtarget (proportional to the target vibration amplitude value) and Vmax, apply a proportional gain to this difference to obtain Δf, and update the drive frequency using the FDwfAnalogOutNodeFrequencySet() function
4. Repeat steps 2 and 3 to control the amplitude consistently.
Our goal is to get the frequency control period to 40 µs (or at least within 1 ms). However, currently the FDwfAnalogInStatus and FDwfAnalogInStatusData functions take a considerable amount of time, resulting in a control cycle period of approximately 10ms.
Is it possible to implement a solution such as calculating the peak sensor output voltage in the FPGA to achieve the desired processing time? If there are any other ways to reduce the processing time, I would appreciate any suggestions.
Additionally, do I need to purchase an FPGA board for this purpose?
I apologize for my poor English and limited knowledge, but I would appreciate your response.
The files below are what I used:
import time # To handle timing operations
import ctypes # To use C libraries in Python
from sys import platform, path # To handle system path operations
import numpy as np
# Load the Digilent WaveForms SDK shared library using ctypes
dwf = ctypes.cdll.dwf
#------------------------------------------------------------------------------------------------------------------------
# Device connection and disconnection
class Data: # Variables used in the script
handle = ctypes.c_int(0)
name = ""
# Scope settings
sampling_frequency = 100e06
buffer_size = 2801
# Generator settings
gen_frequency = 28.3e03
amplitude = 2.5
run_time = 2
# Control time
vibrating_time = 2
def device_open(): # Open the first connected device
# Specify the address of the connected device (initial value is integer)
device_handle = ctypes.c_int()
# Connect to the first available device by passing -1 as the argument, storing the handle in device_handle
dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(device_handle))
Data.handle = device_handle
return Data
def device_close(device_data): # Close the device
result = dwf.FDwfDeviceClose(device_data.handle)
if result != 1:
print(f"Failed to close device. Error code: {result}")
return result
#------------------------------------------------------------------------------------------------------------------------
# Oscilloscope settings
def scope_open(device_data, sampling_frequency=Data.sampling_frequency, buffer_size=Data.buffer_size, offset=0, amplitude_range=50):
# Enable channel 0
dwf.FDwfAnalogInChannelEnableSet(device_data.handle, ctypes.c_int(0), ctypes.c_bool(True))
# Set offset voltage
dwf.FDwfAnalogInChannelOffsetSet(device_data.handle, ctypes.c_int(0), ctypes.c_double(offset))
# Set maximum voltage
dwf.FDwfAnalogInChannelRangeSet(device_data.handle, ctypes.c_int(0), ctypes.c_double(amplitude_range))
# Set buffer size
dwf.FDwfAnalogInBufferSizeSet(device_data.handle, ctypes.c_int(buffer_size))
# Set sampling frequency (Hz)
dwf.FDwfAnalogInFrequencySet(device_data.handle, ctypes.c_double(sampling_frequency))
# Disable averaging to use raw data
dwf.FDwfAnalogInChannelFilterSet(device_data.handle, ctypes.c_int(-1), ctypes.c_int(0))
return
def scope_record(device_data, channel=1): # Record voltage
dwf.FDwfAnalogInAcquisitionModeSet(device_data.handle, ctypes.c_int(0))
dwf.FDwfAnalogInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(True))
# Store the buffer status
status = ctypes.c_byte()
while True:
# Check acquisition status, 2: keep reading data, 3: store status in the status variable
dwf.FDwfAnalogInStatus(device_data.handle, ctypes.c_bool(True), ctypes.byref(status))
# End when status becomes 2 (data acquisition starts and data is stored in buffer)
if status.value == ctypes.c_ubyte(2).value:
break
# Copy the data stored in the buffer
# Create an empty buffer, with the number of significant digits * buffer size array (all elements initialized to 0)
buffer = (ctypes.c_double * Data.buffer_size)()
# Copy the acquired data array to the buffer
dwf.FDwfAnalogInStatusData(device_data.handle, ctypes.c_int(channel - 1), buffer, ctypes.c_int(Data.buffer_size))
# Convert data to floating point numbers, put them in the buffer list, and get the maximum value
np_buffer = np.ctypeslib.as_array(buffer)
V_sensor = np.max(np.abs(np_buffer))
return V_sensor
def scope_close(device_data):
result = dwf.FDwfAnalogInReset(device_data.handle)
return
#------------------------------------------------------------------------------------------------------------------------
def generate(device_data, channel=1, function=ctypes.c_ubyte(1), offset=0, gen_frequency=Data.gen_frequency, amplitude=Data.amplitude, symmetry=50, wait=0, run_time=Data.run_time, repeat=100000):
# Enable the channel (channel 1 is 0)
channel = ctypes.c_int(channel - 1)
# Enable the carrier wave
dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_bool(True))
# Disable the signal wave (2)
dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, ctypes.c_int(2), ctypes.c_bool(False))
# Set the waveform
dwf.FDwfAnalogOutNodeFunctionSet(device_data.handle, channel, ctypes.c_int(0), function)
# Set the frequency
dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(gen_frequency))
# Set the voltage amplitude 0-p
dwf.FDwfAnalogOutNodeAmplitudeSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(amplitude))
# Set the offset
dwf.FDwfAnalogOutNodeOffsetSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(offset))
# Set the symmetry
dwf.FDwfAnalogOutNodeSymmetrySet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(symmetry))
# Set the waveform generation time (seconds)
dwf.FDwfAnalogOutRunSet(device_data.handle, channel, ctypes.c_double(run_time))
# Set the waiting time before starting
dwf.FDwfAnalogOutWaitSet(device_data.handle, channel, ctypes.c_double(wait))
# Set the number of repetitions
dwf.FDwfAnalogOutRepeatSet(device_data.handle, channel, ctypes.c_int(repeat))
# Start
dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(True))
return
def generate_close(device_data, channel=0):
channel = ctypes.c_int(channel - 1)
dwf.FDwfAnalogOutReset(device_data.handle, channel)
return
#------------------------------------------------------------------------------------------------------------------------
# Parameters
# Target amplitude (p-p) [μm]
A = 5
# Sensor voltage to amplitude conversion formula parameters V (voltage 0-p) = aA (amplitude p-p) + b
a = # Values are entered when driven
b = # Values are entered when driven
# Target voltage [V]
V_target = a * A + b
print(V_target)
# Gain
K_p = 1
#------------------------------------------------------------------------------------------------------------------------
# Create object, connect to device, and configure oscilloscope
device_data = Data()
device_open()
# Device configuration 2: initialize device parameters (False: do not initialize) 3: continuously acquire specified range data
# Start sampling
scope_open(device_data)
# Start generator operation
generate(device_data)
f_now = device_data.gen_frequency
time.sleep(1)
# Start time
t_start = time.time()
# Constant amplitude control
while True:
# Measure the maximum value and calculate the error
V_sensor = scope_record(device_data)
e = V_target - V_sensor
print(e)
# Update amplitude
df = K_p * e
f_new = f_now - df
print(f_new)
dwf.FDwfDeviceAutoConfigureSet(device_data.handle, ctypes.c_int(3))
dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, 0, ctypes.c_int(0), ctypes.c_double(f_new))
f_now = f_new
if time.time() - t_start >= 2:
break
# Disconnect the device
generate_close(device_data)
scope_close(device_data)
device_close(device_data)
Question
towa
Hi Everyone,
I have a question regarding Analog Discovery 2.
I am currently developing a system that uses Analog Discovery 2 to control the frequency of an ultrasonic transducer and keep its vibration amplitude constant.
Specific objectives and implementation details are as follows:
The following process is based on the “Getting Started with WaveForms SDK” website.
1. Drive an ultrasonic transducer with a resonant frequency of 28.24kHz at approximately 28.3kHz using the generate() function.
2. Measure the output voltage of the ultrasonic transducer's pickup sensor using the AD2 oscilloscope function at a sampling frequency of 100MHz for 28µs, and calculate the peak amplitude Vmax using the record() function.
3. Calculate the difference e(t) between the target sensor voltage value Vtarget (proportional to the target vibration amplitude value) and Vmax, apply a proportional gain to this difference to obtain Δf, and update the drive frequency using the FDwfAnalogOutNodeFrequencySet() function
4. Repeat steps 2 and 3 to control the amplitude consistently.
Our goal is to get the frequency control period to 40 µs (or at least within 1 ms). However, currently the FDwfAnalogInStatus and FDwfAnalogInStatusData functions take a considerable amount of time, resulting in a control cycle period of approximately 10ms.
Is it possible to implement a solution such as calculating the peak sensor output voltage in the FPGA to achieve the desired processing time? If there are any other ways to reduce the processing time, I would appreciate any suggestions.
Additionally, do I need to purchase an FPGA board for this purpose?
I apologize for my poor English and limited knowledge, but I would appreciate your response.
The files below are what I used:
import time # To handle timing operations import ctypes # To use C libraries in Python from sys import platform, path # To handle system path operations import numpy as np # Load the Digilent WaveForms SDK shared library using ctypes dwf = ctypes.cdll.dwf #------------------------------------------------------------------------------------------------------------------------ # Device connection and disconnection class Data: # Variables used in the script handle = ctypes.c_int(0) name = "" # Scope settings sampling_frequency = 100e06 buffer_size = 2801 # Generator settings gen_frequency = 28.3e03 amplitude = 2.5 run_time = 2 # Control time vibrating_time = 2 def device_open(): # Open the first connected device # Specify the address of the connected device (initial value is integer) device_handle = ctypes.c_int() # Connect to the first available device by passing -1 as the argument, storing the handle in device_handle dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(device_handle)) Data.handle = device_handle return Data def device_close(device_data): # Close the device result = dwf.FDwfDeviceClose(device_data.handle) if result != 1: print(f"Failed to close device. Error code: {result}") return result #------------------------------------------------------------------------------------------------------------------------ # Oscilloscope settings def scope_open(device_data, sampling_frequency=Data.sampling_frequency, buffer_size=Data.buffer_size, offset=0, amplitude_range=50): # Enable channel 0 dwf.FDwfAnalogInChannelEnableSet(device_data.handle, ctypes.c_int(0), ctypes.c_bool(True)) # Set offset voltage dwf.FDwfAnalogInChannelOffsetSet(device_data.handle, ctypes.c_int(0), ctypes.c_double(offset)) # Set maximum voltage dwf.FDwfAnalogInChannelRangeSet(device_data.handle, ctypes.c_int(0), ctypes.c_double(amplitude_range)) # Set buffer size dwf.FDwfAnalogInBufferSizeSet(device_data.handle, ctypes.c_int(buffer_size)) # Set sampling frequency (Hz) dwf.FDwfAnalogInFrequencySet(device_data.handle, ctypes.c_double(sampling_frequency)) # Disable averaging to use raw data dwf.FDwfAnalogInChannelFilterSet(device_data.handle, ctypes.c_int(-1), ctypes.c_int(0)) return def scope_record(device_data, channel=1): # Record voltage dwf.FDwfAnalogInAcquisitionModeSet(device_data.handle, ctypes.c_int(0)) dwf.FDwfAnalogInConfigure(device_data.handle, ctypes.c_bool(False), ctypes.c_bool(True)) # Store the buffer status status = ctypes.c_byte() while True: # Check acquisition status, 2: keep reading data, 3: store status in the status variable dwf.FDwfAnalogInStatus(device_data.handle, ctypes.c_bool(True), ctypes.byref(status)) # End when status becomes 2 (data acquisition starts and data is stored in buffer) if status.value == ctypes.c_ubyte(2).value: break # Copy the data stored in the buffer # Create an empty buffer, with the number of significant digits * buffer size array (all elements initialized to 0) buffer = (ctypes.c_double * Data.buffer_size)() # Copy the acquired data array to the buffer dwf.FDwfAnalogInStatusData(device_data.handle, ctypes.c_int(channel - 1), buffer, ctypes.c_int(Data.buffer_size)) # Convert data to floating point numbers, put them in the buffer list, and get the maximum value np_buffer = np.ctypeslib.as_array(buffer) V_sensor = np.max(np.abs(np_buffer)) return V_sensor def scope_close(device_data): result = dwf.FDwfAnalogInReset(device_data.handle) return #------------------------------------------------------------------------------------------------------------------------ def generate(device_data, channel=1, function=ctypes.c_ubyte(1), offset=0, gen_frequency=Data.gen_frequency, amplitude=Data.amplitude, symmetry=50, wait=0, run_time=Data.run_time, repeat=100000): # Enable the channel (channel 1 is 0) channel = ctypes.c_int(channel - 1) # Enable the carrier wave dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_bool(True)) # Disable the signal wave (2) dwf.FDwfAnalogOutNodeEnableSet(device_data.handle, channel, ctypes.c_int(2), ctypes.c_bool(False)) # Set the waveform dwf.FDwfAnalogOutNodeFunctionSet(device_data.handle, channel, ctypes.c_int(0), function) # Set the frequency dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(gen_frequency)) # Set the voltage amplitude 0-p dwf.FDwfAnalogOutNodeAmplitudeSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(amplitude)) # Set the offset dwf.FDwfAnalogOutNodeOffsetSet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(offset)) # Set the symmetry dwf.FDwfAnalogOutNodeSymmetrySet(device_data.handle, channel, ctypes.c_int(0), ctypes.c_double(symmetry)) # Set the waveform generation time (seconds) dwf.FDwfAnalogOutRunSet(device_data.handle, channel, ctypes.c_double(run_time)) # Set the waiting time before starting dwf.FDwfAnalogOutWaitSet(device_data.handle, channel, ctypes.c_double(wait)) # Set the number of repetitions dwf.FDwfAnalogOutRepeatSet(device_data.handle, channel, ctypes.c_int(repeat)) # Start dwf.FDwfAnalogOutConfigure(device_data.handle, channel, ctypes.c_bool(True)) return def generate_close(device_data, channel=0): channel = ctypes.c_int(channel - 1) dwf.FDwfAnalogOutReset(device_data.handle, channel) return #------------------------------------------------------------------------------------------------------------------------ # Parameters # Target amplitude (p-p) [μm] A = 5 # Sensor voltage to amplitude conversion formula parameters V (voltage 0-p) = aA (amplitude p-p) + b a = # Values are entered when driven b = # Values are entered when driven # Target voltage [V] V_target = a * A + b print(V_target) # Gain K_p = 1 #------------------------------------------------------------------------------------------------------------------------ # Create object, connect to device, and configure oscilloscope device_data = Data() device_open() # Device configuration 2: initialize device parameters (False: do not initialize) 3: continuously acquire specified range data # Start sampling scope_open(device_data) # Start generator operation generate(device_data) f_now = device_data.gen_frequency time.sleep(1) # Start time t_start = time.time() # Constant amplitude control while True: # Measure the maximum value and calculate the error V_sensor = scope_record(device_data) e = V_target - V_sensor print(e) # Update amplitude df = K_p * e f_new = f_now - df print(f_new) dwf.FDwfDeviceAutoConfigureSet(device_data.handle, ctypes.c_int(3)) dwf.FDwfAnalogOutNodeFrequencySet(device_data.handle, 0, ctypes.c_int(0), ctypes.c_double(f_new)) f_now = f_new if time.time() - t_start >= 2: break # Disconnect the device generate_close(device_data) scope_close(device_data) device_close(device_data)
Link to comment
Share on other sites
5 answers to this question
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now