from ..core import Register
from ..utils import int_to_list, from_voltage
from .I2CController import I2CController
[docs]class DAC53401(I2CController):
"""Class for the I2C DAC chip DAC53401.
Subclass of the I2CController class. Attributes and methods below are
differences in this class from I2CController only.
Attributes
----------
ADDRESS_HEADER : int
7-bit device address with R/W bit space and 4 MSBs filled in specific
to this chip. The rest is left as 0 to be filled in later.
registers : dict
Name-Register pairs for the internal registers of the TCA9555 chip.
addr_pins : int
3 LSBs of the 7-bit device address formed alongside the address header
used to differentiate between different instances of the TCA9555 chip.
"""
ADDRESS_HEADER = 0b10010000
registers = Register.get_chip_registers('DAC53401')
# Address Pins Guide
# Slave Address | A0 Pin
# 000 | AGND
# 001 | VDD
# 010 | SDA
# 011 | SCL
[docs] def write(self, data, register_name='DAC_DATA'):
"""Write data to any register on the chip."""
dev_addr = DAC53401.ADDRESS_HEADER | (self.addr_pins << 1)
register = DAC53401.registers[register_name]
data <<= register.bit_index_low
# Mask only the bits for the specified register.
mask = 0
for bit in range(register.bit_index_high, register.bit_index_low - 1, -1):
mask |= 0x1 << bit
# Compare with current data to mask unchanged values
read_out = self.read(register_name)
if read_out == None:
print('Read for masking FAILED')
return False
new_data = (data & mask) | (read_out & ~mask)
# Turn data into a list of bytes for i2c_write method
# Essentially round up to the closest byte
#number_of_bytes = (register.bit_width + 7) // 8
list_data = int_to_list(new_data)
number_of_bytes = len(list_data)
self.i2c_write_long(
dev_addr, [register.address], number_of_bytes, list_data)
[docs] def read(self, register_name='DAC_DATA'):
"""Return data from any register on the chip."""
dev_addr = DAC53401.ADDRESS_HEADER | (self.addr_pins << 1)
register = DAC53401.registers[register_name]
# Ex. 16-bit register: | Byte 1 [15:8] | Byte 0 [7:0] |
byte_number = register.bit_index_high // 8
# 16-bit register = 2 bytes, i2c_read_long starts at the MSB so we read 2 bytes to get Byte 0
number_of_bytes = 2 - byte_number
read_back_list = self.i2c_read_long(
dev_addr, [register.address], number_of_bytes)
# Turn the list into an integer
read_back_data = 0
if read_back_list == None:
return None
# First byte in the list is the MSB, shift and append the next byte
for byte in read_back_list:
print('Readback byte of {:02X}'.format(byte))
read_back_data <<= 8
read_back_data |= byte
# Get only the bits for the specified register from what was read back.
desired_bits = 0
for bit in range(register.bit_index_high, register.bit_index_low - 1, -1):
desired_bits += 0x1 << bit
desired_data = (read_back_data
& desired_bits) >> register.bit_index_low
return desired_data
[docs] def write_voltage(self, voltage):
"""Write a voltage output from the DAC."""
voltage_data = from_voltage(
voltage=voltage, num_bits=10, voltage_range=5, with_negatives=False)
self.write(voltage_data, 'DAC_DATA')
[docs] def enable_internal_reference(self):
"""Enable the DAC's internal reference.
This is necessary to set the gain value.
"""
self.write(0x1, 'REF_EN')
[docs] def set_gain(self, gain):
"""Set the DAC's output gain value.
Gain can be set to 1.5x, 2x, 3x, or 4x the internal reference.
Internal reference must be enabled.
"""
gain_dict = {
1.5: 0b00,
2: 0b01,
3: 0b10,
4: 0b11
}
gain_code = gain_dict.get(gain)
if gain_code == None:
print('Invalid gain')
return False
self.write(gain_code, 'DAC_SPAN')
[docs] def get_gain(self):
"""Return the DAC's current output gain value."""
gain_dict = {
0b00: 1.5,
0b01: 2,
0b10: 3,
0b11: 4
}
gain_code = self.read('DAC_SPAN')
return gain_dict.get(gain_code)
[docs] def config_func(self, function_name):
"""Configure the function generator.
'triangle': Triangle wave between MARGIN_HIGH code to MARGIN_LOW code
with slope defined by SLEW_RATE.
'sawtooth_falling': Saw-Tooth wave between MARGIN_HIGH code to
MARGIN_LOW code with slope defined by SLEW_RATE and immeidate falling edge.
'sawtooth_rising': Saw-Tooth wave between MARGIN_HIGH code to
MARGIN_LOW code with slope defined by SLEW_RATE and immeidate rising edge.
'square': Square wave between MARGIN_HIGH code to MARGIN_LOW code with
pulse high and low period defined by defined by SLEW_RATE.
"""
func_dict = {
'triangle': 0b00,
'sawtooth_falling': 0b01,
'sawtooth_rising': 0b10,
'square': 0b11
}
func_code = func_dict.get(function_name.lower())
if func_code == None:
print('Invalid function name')
return False
self.write(func_code, 'FUNC_CONFIG')
[docs] def start_func(self):
"""Start function generation.
Waveform configured through config_func() and config_margins() functions.
"""
self.write(0b1, 'START_FUNC_GEN')
[docs] def stop_func(self):
"""Stop function generation."""
self.write(0b0, 'START_FUNC_GEN')
[docs] def config_margins(self, margin_high=None, margin_low=None):
"""Configure margin high and low values."""
if margin_high != None:
self.write(margin_high, 'MARGIN_HIGH')
if margin_low != None:
self.write(margin_low, 'MARGIN_LOW')
[docs] def config_step(self, step):
"""Configure the number of bits to step through."""
step_dict = {
1: 0b000,
2: 0b001,
3: 0b010,
4: 0b011,
6: 0b100,
8: 0b101,
16: 0b110,
32: 0b111
}
step_code = step_dict.get(step)
if step_code == None:
print('Invalid step')
return False
self.write(step_code, 'CODE_STEP')
[docs] def config_rate(self, rate):
"""Configure the rate to step through each bit."""
# Instead of asking the user to type in the timing, the rates are organized slowest to fastest and numbered 1-16
rate_dict = {
1: 0b1011, # 1638.4 * 1.75 us
2: 0b1010, # 1638.4 * 1.50 us
3: 0b1001, # 1638.4 * 1.25 us
4: 0b1000, # 1638.4 us
5: 0b0111, # 204.8 * 1.75 us
6: 0b0110, # 204.8 * 1.50 us
7: 0b0101, # 204.8 * 1.25 us
8: 0b0100, # 204.8 us
9: 0b0011, # 25.6 * 1.75 us
10: 0b0010, # 25.6 * 1.50 us
11: 0b0001, # 25.6 * 1.25 us
12: 0b0000, # 25.6 us
13: 0b1100, # 12 us
14: 0b1101, # 8 us
15: 0b1110, # 4 us
16: 0b1111 # None
}
rate_code = rate_dict.get(rate)
if rate_code == None:
print('Invalid rate')
return False
self.write(rate_code, 'SLEW_RATE')
[docs] def reset(self):
"""Reset the chip using a software reset."""
self.write(0b1010, 'SW_RESET')
[docs] def lock(self):
"""Lock the registers of the device so they cannot be changed."""
self.write(0b1, 'DEVICE_LOCK')
[docs] def unlock(self):
"""Unlock the registers of the device so they can be changed again."""
self.write(0b0101, 'DEVICE_UNLOCK_CODE')
[docs] def power_up(self):
"""Power up the DAC output."""
self.write(0b00, 'DAC_PDN')
[docs] def power_down_10k(self):
"""Power down the DAC output to 10K ohms."""
self.write(0b01, 'DAC_PDN')
[docs] def power_down_high_impedance(self):
"""Power down the DAC output to high impedance (default)."""
self.write(0b10, 'DAC_PDN')
[docs] def get_id(self):
"""Return the device ID and version ID as one integer."""
device_id = self.read('DEVICE_ID')
version_id = self.read('VERSION_ID')
return (device_id << DAC53401.registers['DEVICE_ID'].bit_index_low) | (version_id << DAC53401.registers['VERSION_ID'].bit_index_low)