D5b example notebook

Example notebook of the D5b, 8 channel 18-bit module. The module contains the same DACs as in the 16 channel D5a module, but it also contains an ARM microcontroller. This allows for operations where exact timing is needed.

At the moment of writing it supports setting the DACs in a normal DC mode, or allows them to toggle between two values with fixed and precise timing. This toggling happens after an external trigger (by another module for example). It can be used in conjuction with a B2 or D4b module to act as a lock-in system. The D5b sends out triggers at every DAC toggle, this allows the B2/D4a to sync the measurements to this change


SPI Rack setup

To use the D5b module, we need to import both the D5b_module and the SPI_rack module from the spirack library. All the communication with the SPI Rack runs through the SPI_rack object which communicates through a virtual COM port. This COM port can only be open on one instance on the PC. Make sure you close the connection here before you can use it somewhere else.

Import the logging library to be able to display the logging messages.


In [ ]:
# Import SPI rack and D5b module
from spirack import SPI_rack, D5b_module
import logging

In [ ]:
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

Open the SPI rack connection and unlock the controller. This is necessary after bootup of the controller module. If not unlocked, no communication with the modules can take place. The virtual COM port baud rate is irrelevant as it doesn't change the actual speed. Timeout can be changed, but 1 second is a good value.


In [ ]:
COM_port = 'COM4' # COM port of the SPI rack
COM_speed = 1e6   # Baud rate, not of much importance
timeout = 1       # Timeout value in seconds

spi_rack = SPI_rack(COM_port, COM_speed, timeout)
spi_rack.unlock() # Unlock the controller to be able to send data to the rack

In [ ]:
spi_rack.unlock()

Read back the version of the microcontroller software. This should return 1.6 or higher to be able to use the D5b properly. Als read the temperature and the battery voltages through the C1b, this way we verify that the connection with the SPI Rack is working.


In [ ]:
print('Version: ' + spi_rack.get_firmware_version())
print('Temperature: {:.2f} C'.format(spi_rack.get_temperature()))
battery_v = spi_rack.get_battery()
print('Battery: {:.3f}V, {:.3f}V'.format(battery_v[0], battery_v[1]))

Create a new D5b module object at the correct module address using the SPI object. By default the module resets the output voltages to 0 Volt. Before it does this, it will read back the current value. If this value is non-zero it will slowly ramp it to zero. If reset_voltages = False then the output will not be changed.

To see that the we have a connection, we read back the firmware version.


In [ ]:
d5b = D5b_module(spi_rack, module=1, reset_voltages=True)
print("Firmware version: {}".format(d5b.get_firmware_version()))

Configuring the D5b

The D5b module can run from either a local (inside the module) clock or a user provided clock from the backplane. This backplane clock should be 10 MHz and either a square or a sine wave. If there are more modules with microcontrollers in the rack, and they need to run synchronously, it is recommended to use the backplane clock. For a single module it is fine to run it using the local clock.

If the external clock is selected but not present, the user will get an ERROR to the logger and the microcontroller will keep running on the internal clock. Never turn off the external clock if the microcontroller is running on it. This will stop the module from functioning.

In this example we will use the internal clock:


In [ ]:
d5b.set_clock_source('internal')
print("Clock source: {}".format(d5b.get_clock_source()))

The toggle time of the DACs is set in steps of 100 ns (the 10 MHz clock) with a minimum of 30 μs. We need to input a value as a multiple of this 100 ns. The toggle amount should be an even number with a minimum of two.


In [ ]:
d5b.set_toggle_time(300)
toggle_value = d5b.get_toggle_time()
print('Toggle time: {} x 100 ns = {} s'.format(toggle_value, round(toggle_value*100e-9, 7)))

d5b.set_toggle_amount(6)
print('Toggle amount: {}'.format(d5b.get_toggle_amount()))

The module will start toggling after it receives a trigger from the backplane (either directly controlled from the PC or from another module). If there are any filters and delays in the setup, we might want to wait with toggling the DAC before these are settled. This is what the hold-off time is for. It can be set in steps of 100 ns, with a minimum of 30 μs. This time should be set in seconds.


In [ ]:
d5b.set_trigger_holdoff_time(30e-6)
print('Holdoff time: {} s'.format(d5b.get_trigger_holdoff_time()))

Running the D5b

We will now set two DAC outputs to toggling mode and &pm4V span. DAC 0 we will set to toggle &pm2V around 0V and DAC1 to toggle between &pm2V around 1V.

For more details on the span, stepsize and voltages. See the documentation on the website and the D5a module example notebook.


In [ ]:
DAC = 0

d5b.set_DAC_span(DAC, '4V_bi')
print("Span DAC {}: {}".format(DAC, d5b.get_DAC_span(DAC)))

d5b.set_DAC_mode(DAC, 'toggle')
print("DAC {} mode: {}\n".format(DAC, d5b.get_DAC_mode(DAC)))

d5b.set_DAC_voltage(DAC, 0)
d5b.set_DAC_neg_toggle_voltage(DAC, -2)
d5b.set_DAC_pos_toggle_voltage(DAC, 2)
values = d5b.get_DAC_voltages(DAC)
print('Voltage: {:.3f} V\nNegative Toggle: {:.3f} V\nPositive Toggle: {:.3f} V'.format(values[0],values[1],values[2]))

In [ ]:
DAC = 1

d5b.set_DAC_span(DAC, '4V_bi')
print("Span DAC {}: {}".format(DAC, d5b.get_DAC_span(DAC)))

d5b.set_DAC_mode(DAC, 'toggle')
print("DAC {} mode: {}\n".format(DAC, d5b.get_DAC_mode(DAC)))

d5b.set_DAC_voltage(DAC, 1)
d5b.set_DAC_neg_toggle_voltage(DAC, -1)
d5b.set_DAC_pos_toggle_voltage(DAC, 3)
values = d5b.get_DAC_voltages(DAC)
print('Voltage: {:.3f} V\nNegative Toggle: {:.3f} V\nPositive Toggle: {:.3f} V'.format(values[0],values[1],values[2]))

Here we generate the trigger directly from the PC to demonstrate the usage.


In [ ]:
spi_rack.trigger_now()

You should be able to see the following output on your oscilloscope (minus the two top traces):

When done with this example, it is recommended to close the SPI Rack connection. This will allow other measurement scripts to access the device.


In [ ]:
spi_rack.close()

In [ ]: