Jump to content

Question

Posted

Hey,

i would like to use SIMULTANEOUS Output for the USB-3104 (master) board, and the USB-3114 (slave) board.
For the master-board i used the function: ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 0).
For the slave-board i used the function:ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 1)

I connected the Pins 49 of the two boards and update the master board with the function:
ul.a_out_scan(self._instacal_board_num, lowest_channel_num, highgest_channel_num,
              range_of_channels, 0,
              self._voltage_range, self.memhandle, ScanOptions.SIMULTANEOUS)

My slave-board (USB-3114) receives the trigger on SYNCLD but doesnt update. How do i get it to do so and how do i assign the right values?

Greetings

Dennis

12 answers to this question

Recommended Posts

  • 0
Posted

Hello @Dennis_.

In your code, you specified the same board number for the master and slave devices.

2 hours ago, Dennis_ said:

For the master-board i used the function: ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 0).
For the slave-board i used the function:ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 1)

Please update the board_num() parameter for the slave-board.

 

image.png

 

  • 0
Posted (edited)

Hey Fausto,

thanks for your reply, the master-board (USB-3104) has the self._instacal_board_num = 1. The slave-board has the self._instacal_board_num = 2 due to objectoriented implementation in the code. The slave-board receives the trigger of the master board on pin-49, when i use the function 

ul.a_out_scan(1, lowest_channel_num, highgest_channel_num,
              range_of_channels, 0,
              self._voltage_range, self.memhandle, ScanOptions.SIMULTANEOUS)

Board 2 (USB-3114, slave-board) doesn't update any values and keeps delivering 0 V. I didn't assign any values to the board that it could update, how do i do so? And how do i connect the trigger, that it updates the assigned values at the right moment?

 

Greetings,

Dennis

 

Edited by Dennis_
  • 0
Posted

Hello @Dennis_.

Please verify in InstaCal that the USB-3104 board is designated as Board#1 (self._instacal_board_num = 1) and the SYNCLD (pin 49) for Simultaneous Mode is set to Master (output).  Additionally, verify in InstaCal that the USB-3114 board is designated as Board#2 (self._instacal_board_num = 2) and the SYNCLD (pin 49) for Simultaneous Mode is set to Slave (input).

image.png

 

  • 0
Posted (edited)

Hello @Fausto,

here are my instacal settings:

grafik.png.c6f987011d38cfaae19a6306a170f0a0.pnggrafik.png.644837936c7483540e666d390913746a.png

 

When i update the USB-3104 (1, master), i create the red line, the blue line is the signal that is on the connected pin 49. the grounds of the two devices are connected aswell. The cannels on the USB-3114 (2, slave) still do not update with any values.

receivedsignals.thumb.png.280213fb6d54891a4831a05d5b89c347.png

 

Regards,

Dennis

Edited by Dennis_
  • 0
Posted

Hello @Dennis_.

Please verify the wire used for the sync signal between pin 49 on each device is secured by the screw terminals.

In your code, start the slave board first, awaiting the clock from the master.  Once the master board begins acquiring data, its output A/D clock is used by the slave boards so that all channels are clocked synchronously.

If the slave device continues unresponsive, swap the slave and master devices, and retest your application.

Regards,

Fausto

  • 0
Posted

Hello @Fausto,

the SIMULTANEOUS update ist working now, thank you. 

I have another problem regarding the updatetime of the SIMULATANEOUS update. If i only update the USB-3104 (blue) with an update-rate of 10 ms it works with a small deviation of up to 1 ms.

onlyUSB-3104.thumb.png.64fb9e16da0529ac71fbbcfe83ac3f9b.png

With the SIMULTANEOUS-mode i get a synchronous update-time of around 17 ms (USB-3104: blue, master; USB-3114: red, slave). Is there an option to accelerate the update-rate to around 10 ms?

SYNCHRONOUSUSB-3104andUSB-3114.thumb.png.c05e11905cfad397a978dc6b3e834024.png

 

Regards,

Dennis

  • 0
Posted

Hello @Dennis_.

What are your current software and PC configurations?

Which development environment and operating system are you using, and what are their versions?

Please provide any other details regarding your setup that may impact your results.

Regards,

Fausto

  • 0
Posted (edited)

Hello @Fausto,

here some specifications:

Laptop, AMD Ryzen 7 5700U with radeon Graphics 1,8 GHz 

512 GB SSD (capacity 476 GB)

1 socket

8 cores

16 logical processors

16 GB RAM of which 14,8 GB are usable 

64-Bit operating-system

Windows 11 Pro, Version24H2 

Windows Feature Experience Pack 1000.26100.48.0

 

For the digilent devices i am using instacal, i have one more board connected, but not used for timetest of 17 ms (USB-1608GX instacal board_num = 0). The USB-distribution is done through an usb-hub from LogiLink. 

grafik.thumb.png.1765c643fd39800930adb08e737d4b15.png

Later on i want to conncect two more E-DIO24 boards over ethernet.

For coding i use python 3.10.10 in visual studio-code 1.97.1

 

If you need more specifications please tell me.

 

Greetings,

Dennis

 

Edited by Dennis_
  • 0
Posted

Hello @Fausto,

i took the USB-Hub out of my structure and connected the devices directly to the laptop, the delay of 17 ms persits. When i add the USB-1608GX, my delay goes up to 20 ms. Do you have some suggestions? 

I would be happy to hear from you.

Regards,

Dennis

  • 0
Posted

Hello @Dennis_.

The USB-3104's analog output throughput rate is 100 Hz per channel max and is system-dependent; some deviation is possible. The daq device does not have a clock and relies on a clock signal from the host system.  How are you physically connecting the USB-1608GX module with the USB-3104 module?  How do you have it programmed in your code?

Regards,

Fausto

  • 0
Posted (edited)

Hello @Fausto,

nice to here from you. The USB-1608GX is connected to the same USB-Hub as the USB-3104 and the USB-3114. To measure the output channels of the USB-3104 and the USB-3114 some of the output channels are directly connected to the single-ended inputs of the USB-1608GX (cable length up to 30 cm). AGND of all USB-boards are connected with eachother.

The following code shows how i tested the boards. I set a QTimer which activates a callback-method that updates the USB-3104 and USB-3114 with new values every 10 ms and reads a specific amount of values with a specific rate at the USB-1608GX. This code is only for testing purposes, its not perfect, but should be functional.

Here it is:

import sys

import csv

from PyQt5.QtCore import QTimer

from PySide6.QtCore import QTimer

import numpy as np

import matplotlib.pyplot as plt

from mcculw import ul

#to set configuration of board

from mcculw.enums import ULRange,ScanOptions,InfoType,BoardInfo, FunctionType, AnalogInputMode

 

from ctypes import cast, POINTER, c_ushort

import numpy as np

 

from PySide6.QtWidgets import QApplication

 

#wenn channelDaten gemischt vom ADC ausgelsen und gespeichert werden und nicht in einem block vorliegen

from collections import defaultdict

 

timer = None

class PhysComm_out:

    def __init__(self, instacal_board_num, voltage_range, max_dac_value):

       

        self._pins_involt = dict[int, int]()        # in steht für in die ECU rein

        self._instacal_board_num  = instacal_board_num

        self._voltage_range = voltage_range

 

        self.scan_options = ScanOptions.SIMULTANEOUS # schon mit instacal gesetzt, muss ich das auch in code mit ul.set_config setzen????

       

        if self._instacal_board_num == 1:

            ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 0)

            #ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, ScanOptions.SIMULTANEOUS, BoardInfo.DACUPDATEMODE, BoardInfo.DACUPDATEMODE)

        else:

            ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.SYNCMODE, 1)

           

            #ul.set_config(InfoType.BOARDINFO, self._instacal_board_num, 0, BoardInfo.DACUPDATEMODE, ScanOptions.SIMULTANEOUS)

 

        self._max_dac_value = max_dac_value # abhängig von Bit Auflösung

 

    def write_to_slaveboard(self, value_dict):

        for channel, value in value_dict.items():

            converted_value = np.uint16(int((round(self._max_dac_value/10 * value))))

            #ul.out_byte(self._instacal_board_num, channel*2, converted_value)


 

       

 

    def convert_for_outputbuffer(self, simultaneous_dict):

 

        '''for both dacs together'''

        # Maximaler Key aus dict1

        #max_key_output_dict = 7

 

        # dict2 um (max_key_dict1 + 1) verschieben

        #output_dict2_shifted = {k + max_key_output_dict + 1: v for k, v in output_dict2.items()}

 

        # Beide Dictionaries zusammenführen

        #simultaneous_dict = {**output_dict, **output_dict2_shifted}

 

        memhandle = ul.win_buf_alloc(self.number_of_values)

        if not memhandle:

            raise Exception("Memory handle allocation failed")

       

        #pointer auf meinen ausgabearray im speicher, umgehung der Methode win_array_to_buf

        output_data = cast(memhandle, POINTER(c_ushort))  

       

        for key in range(self.lowest_channel_num, self.number_of_values):

            # test if key is missing in dictionary

            #if key not in simultaneous_dict:

 

                # Füge den fehlenden Schlüssel mit dem Standardwert hinzu

                #simultaneous_dict[key] = default_value

           

            #print(str(output_dict[key]))

            #Werte des dicts zu passendem Format für Ausgabe in a_out_scan kovertieren

            converted_value = int(round(self._max_dac_value/10 * simultaneous_dict[key])) # skalieren auf die 10 V und möglichtst genau runden

            #print(str(np.uint16(converted_value)))

 

            if 0<=converted_value<=self._max_dac_value:    

                simultaneous_dict[key] = np.uint16(converted_value)

            else:

                print("failure")

 

        #sortieren des output dicts nach channels, anschließend Werte in eine liste konvertieren

        output_list = list(dict(sorted(simultaneous_dict.items(), key=lambda item: item[0])).values())

        #print(output_list)

 

        #werte zu output_data pointer hinzufügen

        for i in range(len(output_list)):

            output_data[i] = output_list[i]


 

        return memhandle#, memhandle2 brauche ich wohl nicht

 

    def send_data_to_dac (self, voltage_dict):

 

        #später sollten die votage dicts einfach die alten Werte behalten wenn niht geupdatet und nur einmal initialisiert werden

        self.number_of_values = len(voltage_dict)

        self.lowest_channel_num = min(list(voltage_dict.keys()))

        self.highgest_channel_num = max(list(voltage_dict.keys()))

        self.range_of_channels = self.highgest_channel_num - self.lowest_channel_num + 1

 

        output_dict = voltage_dict

        #output_dict2 = voltage_dict2

                 

        self.memhandle = self.convert_for_outputbuffer(output_dict)                    

        try:

            ul.a_out_scan(self._instacal_board_num, self.lowest_channel_num, self.highgest_channel_num,

                        self.range_of_channels, ULRange.NOTUSED,#self.range_of_channels*100, self._voltage_range, self.memhandle, self.scan_options)

        except Exception as e:

            print(f"Fehler aufgetreten: {e}")

        finally:

            # Speicher freigeben, wenn der Vorgang abgeschlossen ist

            ul.win_buf_free(self.memhandle)

            #ul.win_buf_free(self.memhandle2)


 

class PhysComm_in:          # erstmal nur Struktur, zusammenfassung der Klassen und besserer Aufbau erfolgt später

 

    def __init__(self, instacal_board_num):

       

        #self._pins_outvolt = dict[int, int]()   # out steht für aus der ECU raus

        #self.adc_volts_dict = dict[int, int]()

        self.adc_volts_dict = defaultdict(list)

        self._instacal_board_num  = instacal_board_num

        ul.a_input_mode(self._instacal_board_num, AnalogInputMode.SINGLE_ENDED)

        #unbenutze channel noch auf GND setzen!

        self._low_channel = 0

        self._high_channel = 11 # 12 channel

        self._points_per_channel = 300 #habe jetzt je channel angenommen (8 channel)

        self._rate = 40000

        self._ul_range = ULRange.BIP10VOLTS

        self._options = ScanOptions.BACKGROUND

        self._resolution = 65635        # 16-bit also 2^16, die 0 ist dann nicht relevant

        self._count = self._points_per_channel * (self._high_channel - self._low_channel + 1)

        self._dict_ADC_to_function = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14, 15: 15}

        self.MEANforChannels = defaultdict (list)

 

    def convert_buffer_input (self, raw_data_from_ADC):

        #Werte von letztem Zyklus verwenden

        for array_value_number in range(self._count):

            self.adc_volts_dict[self._dict_ADC_to_function[array_value_number%12]].append(raw_data_from_ADC[array_value_number])               

        for channels, values in self.adc_volts_dict.items():

            if values:  # Überprüfen, ob die Liste nicht leer ist

                mean_value = ((sum(values) / len(values)) - 32767.5) / 3276.75  # Mittelwert berechnen und in Volt skaliert ausgeben

                #print(f"Schlüssel: {channels}, Mittelwert: {mean_value}")

                self.MEANforChannels[channels].append(mean_value)

            else:

                print(f"Schlüssel: {channels} hat keine Werte, Mittelwert kann nicht berechnet werden.")

       

        self.adc_volts_dict = defaultdict(list)          

 

    def receive_data_from_adc (self):

 

        if hasattr(self, 'receiving_data_array'):                                    #wenn receiving_memhandle existiert

 

            self.convert_buffer_input(self.receiving_data_array)

            # Free the buffer and set the data_array to None

            ul.win_buf_free(self.receiving_memhandle_ADC)

            self.receiving_data_array = None


 

        self.receiving_memhandle_ADC = ul.win_buf_alloc(self._count)

        self.receiving_data_array = cast(self.receiving_memhandle_ADC, POINTER(c_ushort))

 

        # Run the input scan

        ul.a_in_scan(self._instacal_board_num, self._low_channel, self._high_channel, self._count, self._rate, self._ul_range, self.receiving_memhandle_ADC, self._options)

 

#------------------------------------------------------------------------------------------------------------------------------

 

class MultiVoltage_calc:

 

    rand_min = -1

    rand_max = 1

 

    def __init__(self, max_duration, time_step, num_voltages_per_dac, dac : PhysComm_out, dac2 : PhysComm_out, adc : PhysComm_in):

        self.time_step = time_step  # Intervall zwischen Updates (10 ms)

        self.num_points = int(max_duration / time_step)  # Anzahl der Punkte

        self.num_voltages = num_voltages_per_dac  # Anzahl der Spannungsverläufe

        self.max_duration = max_duration

        self.current_index = 0  # Startindex

 

        self.current_voltage_values = [3, 3, 3, 3, 3, 3, 3, 3] #anpassen wenn ich Anzahl der Spannungen verändere

        self.current_voltage_values2 = [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7] #anpassen wenn ich Anzahl der Spannungen verändere

        self.dac_channels = [0,1,2,3,4,5,6,7] #anpassen wenn ich Anzahl der Spannungen verändere, verwende ich nur beim Zeitpunkt speichern oder???

 

        self.dac_volt_dict = dict[int, int]()

        self.dac2_volt_dict = dict[int, int]()

 

        self.dac = dac

        self.dac2 = dac2

        self.adc = adc

 

        self.all_send_volt_dict = {}  # Leeres Dictionary

 

        self.geschw_test = 0

       

 

    def update_values(self):

        """Berechnet und speichert neue zufällige Werte für alle Spannungsverläufe"""

        if self.current_index < self.num_points:

 

            #-----------------------------------------------------------------------------------------

            #jeden channel einzeln auf konstanten wert setzen

            '''self.current_voltage_values = {0:2, 1:8, 2:4, 3:0, 4:0, 5:0, 6:0, 7:0} #dict, key ist channel, value ist Spannungsebene in Volt'''

            #------------------------------------------------------------------------------------------

            for i in range(self.num_voltages):

                #---------------------------------------------------------------------------------------------

                # for random value

                random_value = np.random.uniform(MultiVoltage_calc.rand_min, MultiVoltage_calc.rand_max)

                self.current_voltage_values[i] += random_value  # Wert für DAC1

                self.current_voltage_values2[i] += random_value # Wert für DAC2

       

                if 0 < self.current_voltage_values[i] < 10:

                    pass

                else:

                    self.current_voltage_values[i] -= 2 * random_value

 

                if 0 < self.current_voltage_values2[i] < 10:

                    pass

                else:

                    self.current_voltage_values2[i] -= 2 * random_value


 

                #----------------------------------------------------------------------------------------------

 

                # for test of precission in combination with time, all channels have the same values

                # modulo to select arranged_values,

                # order is 0 V -> 10 V -> 1 V -> 5 V -> 9 V, and starting from beginning

                selected_value =self.current_index % 5

 

                if selected_value == 0:

                    arranged_value = 0

                elif selected_value == 1:

                    arranged_value = 5

                elif selected_value == 2:

                    arranged_value = 2

                elif selected_value == 3:

                    arranged_value = 7

                elif selected_value == 4:

                    arranged_value = 10

                else:

                    print("Der Wert liegt außerhalb des gültigen Bereichs.")

               

                self.current_voltage_values[1] = arranged_value  # Wert hinzufügen

                #--------------------------------------------------------------------------------------------

                #Kalibrierung 0 V / 10 V

                '''k = i%2

                if k ==1:

                    self.current_voltage_values[i] = 0

                else:

                    self.current_voltage_values[i] = 10'''

 

                #----------------------------------------------------------------------------------------------

                '''#dac1

                #trigger every 10 ms, switch in between 2 V and 4 V

                trigger = self.current_index % 2

                if trigger == 0:

                    self.current_voltage_values[0] = 2 # für DAC-3104, also dict 1

                    self.current_voltage_values[1] = 2

                else:

                    self.current_voltage_values[0] = 4

                    self.current_voltage_values[1] = 4

 

                #dac2

                #trigger every 10 ms, switch in between 4 V and 8 V

                if trigger == 0:

                    self.current_voltage_values2[0] = 6 # für DAC-3114, also dict 2

                else:

                    self.current_voltage_values2[0] = 8'''

                #-----------------------------------------------------------------------------------------------

                #DAC Spannungen schreiben und zeitverläufe behalten

                # i ist der channel, current_index ist der jeweilige Zeitpunkt, alle 10 ms

                self.dac_volt_dict[self.dac_channels[i]] = self.current_voltage_values[i]  

                self.all_send_volt_dict[i, self.current_index] = self.current_voltage_values[i]

                self.all_send_volt_dict[i+8, self.current_index] = self.current_voltage_values2[i]

 

                self.dac2_volt_dict[self.dac_channels[i]] = self.current_voltage_values2[i]

            self.dac2.send_data_to_dac(self.dac2_volt_dict)

            self.dac.send_data_to_dac(self.dac_volt_dict)

 

            #ADC

            # Mittelwert von self.adc_volts_dict berechnen und irgendwie über alle verläufe behalten, also appenden

            self.adc.receive_data_from_adc()

            self.current_index += 1  # Zum nächsten Zeitpunkt wechseln

 

def timer_callback(volts, adc):        

   

    if volts.current_index < volts.num_points:

        volts.update_values()  # Neue Werte berechnen

       

    else:

        timer.stop()

        plot(volts, adc)

        ul.stop_background(1, FunctionType.AOFUNCTION)

        ul.stop_background(0, FunctionType.AIFUNCTION)

        ul.stop_background(2, FunctionType.AOFUNCTION)

        print('Ende!')      

   

 

def main():

    global timer

    max_duration = 4  # Gesamtdauer der Traces in Sekunden

    time_step = 0.01  # Intervall zwischen den Updates (10 ms)

    num_voltages_per_dac = 8  # Anzahl der Spannungsverläufe

 

    dac = PhysComm_out(instacal_board_num = 1, voltage_range = ULRange.BIP10VOLTS, max_dac_value=65535)

    dac2 = PhysComm_out(instacal_board_num = 2, voltage_range = ULRange.BIP10VOLTS, max_dac_value=65535)

 

    adc = PhysComm_in(instacal_board_num = 0)


 

    volts = MultiVoltage_calc(max_duration, time_step, num_voltages_per_dac, dac, dac2, adc)

 

    app = QApplication(sys.argv)

 

    # Timer erstellen

    timer = QTimer()

    timer.timeout.connect(lambda: timer_callback(volts, adc))  # Verbinde das Signal übergebe die errechneten Werte für DAC

    timer.start(10)  # 10 Millisekunden

 

    # Event-Schleife starten

    sys.exit(app.exec())


 

if __name__ == "__main__":

    main()

Edited by Dennis_

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...